为什么UIView在旋转时不会停留在中心位置?

我正在尝试校准UIView,以便在旋转时保持在中心位置。 这是对我前一个问题的最佳答案所确定的问题的跟进。 我已经设计了基于一个答案的代码,建议使用viewWillAppearviewDidLayoutSubviews而不是viewDidLoad

由于我是Swift的新手,我不得不重新访问Swift教程实现自定义控件,该控件描述了如何创建UIView的子类 。 我的代码可以使用以下三个函数中的任何一个绘制一个圆: viewWillAppearviewDidLayoutSubviewsviewDidLoad

但是,当我渲染代码时,当屏幕旋转时,圆圈并不总是保持在中心。 下面是使用viewDidLoadviewWillAppear进行的连续旋转 viewDidLoad或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) } } 

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

  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) } 

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

首先,视图没有在viewdidload和viewwillappear中正确布局,并且你明确地定义了具有值的框架,我发现这是一个非常令人沮丧的新手程序员。 特别是在调整标签大小时,它仍然让我很烦。 因此,放置视图和主屏幕的centreX和centreY意味着当您旋转时,您的视图将保持在该点。

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

 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) 

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

你必须用frame.bounds替换frame.bounds以使中心计算正确。