ios - 为什么 UIView 旋转时不居中?

标签 ios swift uiview

我正在尝试校准 UIView,使其在旋转时保持在中心位置。这是由 my previous question 的最佳答案确定的问题。 .我已经根据 one answer 设计了代码建议使用 viewWillAppearviewDidLayoutSubviews 而不是 viewDidLoad

因为我是 Swift 的新手,所以我不得不重新访问 Swift 教程实现自定义控件,其中描述了 how to make a subClass of UIView .我的代码可以使用以下三个函数中的任何一个绘制一个圆:viewWillAppearviewDidLayoutSubviewsviewDidLoad

然而,当我渲染代码时,随着屏幕旋转,圆圈并不总是保持在中心位置。以下是使用 viewDidLoadviewWillAppear viewDidLoad or viewWillAppear 发生的连续旋转当应用程序在纵向屏幕上启动时,问题会在第一次旋转后立即出现,无论是向左还是向右。如果以横向启动,如果没有尝试至少四次连续旋转,则可能会错过问题。

下面是使用 viewDidLayoutSubviews 发生的连续旋转,其中旋转偶尔会导致中心圆覆盖偏离中心的圆。 viewDidLayoutSubviews这可能会在多次旋转后发生,但必须至少完成四次旋转才能发现问题。

这是 CircleView 的代码 - 我试图创建 UIView 的子类

import UIKit

class CircleView: UIView {
    let radius          = CGFloat(200)
    let x: CGFloat      = 0
    let y: CGFloat      = 0

// MARK: Initialization

required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

    override init(frame: CGRect) {
    super.init(frame: frame)
    setup()
}

func setup() {

    // Create a CAShapeLayer
    let shapeLayer = CAShapeLayer()

    // The Bezier path that we made needs to be converted
    // to a CGPath before it can be used on a layer.

    shapeLayer.path = createBezierPath().cgPath

    // apply other properties related to the path

    shapeLayer.strokeColor      = UIColor.red.cgColor
    shapeLayer.fillColor        = UIColor.white.cgColor
    shapeLayer.lineWidth        = 8.0

    // add the new layer to our custom view
    self.layer.addSublayer(shapeLayer)
}

func createBezierPath() -> UIBezierPath {
    // create a new path

    let path  = UIBezierPath(arcCenter: CGPoint(x: x, y: y),
                radius: radius,
                startAngle: CGFloat(M_PI * -0.5),
                endAngle:CGFloat(M_PI * 1.5),
                clockwise: true)

    path.fill()
    path.stroke()
    return path
    } 
}

这是 ViewController 的代码

import UIKit

class ViewController: UIViewController {
    var circle: CircleView?
    var x: CGFloat?
    var y: CGFloat?

    override func viewDidLoad() {
    super.viewDidLoad()
    let centre = centreCircle()

    // create a new UIView and add it to the view controller
    let circleView = CircleView()
    circleView.frame = CGRect(x: centre.x, y: centre.y, width: 0, height: 0)
    view.addSubview(circleView)
    }

    func centreCircle() -> CGPoint {
    circle?.bounds = UIScreen.main.bounds
    return CGPoint(x: UIScreen.main.bounds.midX, y: UIScreen.main.bounds.midY)
    }
}

这是返回屏幕中心点的 UIScreen 扩展的代码

import UIKit

extension UIScreen {
    var centre: CGPoint {
    return CGPoint(x: bounds.midX, y: bounds.midY)
    }
}

为了产生上述旋转,我将 viewDidLoad 函数替换为 viewWillAppear

    override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    let centre = centreCircle()

    // create a new UIView and add it to the view controller
    let circleView = CircleView()
    circleView.frame = CGRect(x: centre.x, y: centre.y, width: 0, height: 0)
    view.addSubview(circleView)
    }

viewDidLayoutSubviews

    override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    let centre = centreCircle()

    // create a new UIView and add it to the view controller
    let circleView = CircleView()
    circleView.frame = CGRect(x: centre.x, y: centre.y, width: 0, height: 0)
    view.addSubview(circleView)
    }

任何人都可以在我的代码中看到可能阻止旋转工作的问题吗?

最佳答案

首先, View 在 viewdidload 和 viewwillappear 中的布局不正确,并且您明确定义了带有值的框架,作为新手程序员,我发现这非常令人沮丧。尤其是在调整标签大小时,这仍然让我很恼火。因此,将您的 View 和主屏幕的 centerX 和 centerY 放在一起意味着您的 View 将在您旋转时停留在该点。

通过学习自动布局来解决这个问题。起初它很糟糕,但它最终会让你的生活更轻松。看看这个扩展中的方法。当我想将一个 View 居中到另一个 View 时,我会使用它

extension UIView {


    func constrainAndCenter(in view: UIView, withRelativeHeight: CGFloat, withRelativeWidth: CGFloat) -> [NSLayoutConstraint] {
        self.translatesAutoresizingMaskIntoConstraints = false

        let x = NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1, constant: 0)
        let w = NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: view, attribute: .width, multiplier: withRelativeWidth, constant: 0)
        let h = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: view, attribute: .height, multiplier: withRelativeHeight, constant: 0)
        let y = NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0)

        return ([x,w,h,y])
    }
}

// then in viewdidload or whereever
let circleView = CircleView() 
let circleViewConstraints = circleView.constrainAndCenter(in: view,....)
view.addSubview(circleView)
NSLayoutConstraint.activate(circleViewConstraints)

这会将它固定在中间。您也可以在界面构建器中执行此操作。

关于ios - 为什么 UIView 旋转时不居中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40059009/

相关文章:

swift - 在 UIView 中跟踪多个手指

ios - 如何使用 IBInspectable 来控制 subview 的属性?

ios - 找不到 Xcode 链接的项目头文件

ios - 使用 enumerateChildNodesWithName 方法进行高级搜索

ios - 使用依赖项创建FIFO NSOperationQueue

swift - 火力地堡 3 : Ambiguous use of 'observeEventType(_:withBlock:)'

ios - Swift Character + Int 操作

ios - Cordova 5.3.1 和 iOS9 平台 - 我无法从外部源加载图像和脚本

ios - 使用 AutoLayout 在方向更改时更改 UITextView 高度

ios - 如何根据 UIView 背景颜色使用 if 语句