为什么 UIView 在旋转时不居中? [英] Why does UIView not stay on centre while it rotates?

查看:30
本文介绍了为什么 UIView 在旋转时不居中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试校准 UIView 以使其在旋转时保持在中心位置.这是 当应用在纵向屏幕上启动时,问题立即出现在第一次旋转之后,无论向左还是向右.如果以横向启动,如果没有尝试至少四次连续旋转,则可能会错过问题.

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

这是 CircleView 的代码 - 我尝试制作 UIView 的子类

导入 UIKit类 CircleView: UIView {让半径 = CGFloat(200)让 x: CGFloat = 0让 y: CGFloat = 0//MARK: 初始化需要初始化?(编码器aDecoder:NSCoder){fatalError("init(coder:) 尚未实现")}覆盖初始化(框架:CGRect){super.init(框架:框架)设置()}功能设置(){//创建一个 CAShapeLayer让 shapeLayer = CAShapeLayer()//我们制作的贝塞尔路径需要转换//到 CGPath,然后才能在图层上使用.shapeLayer.path = createBezierPath().cgPath//应用与路径相关的其他属性shapeLayer.strokeColor = UIColor.red.cgColorshapeLayer.fillColor = UIColor.white.cgColorshapeLayer.lineWidth = 8.0//将新图层添加到我们的自定义视图中self.layer.addSublayer(shapeLayer)}func createBezierPath() ->UIBezierPath {//创建一个新路径让路径 = UIBezierPath(arcCenter: CGPoint(x: x, y: y),半径:半径,起始角度:CGFloat(M_PI * -0.5),endAngle:CGFloat(M_PI * 1.5),顺时针:真)路径填充()path.stroke()返回路径}}

这里是ViewController的代码

导入 UIKit类视图控制器:UIViewController {var circle:CircleView?var x:CGFloat?var y:CGFloat?覆盖 func viewDidLoad() {super.viewDidLoad()让 center = centerCircle()//创建一个新的 UIView 并将其添加到视图控制器让 circleView = CircleView()circleView.frame = CGRect(x: center.x, y: center.y, width: 0, height: 0)view.addSubview(circleView)}func centerCircle() ->CG点{circle?.bounds = UIScreen.main.bounds返回 CGPoint(x: UIScreen.main.bounds.midX, y: UIScreen.main.bounds.midY)}}

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

导入 UIKit扩展 UIScreen {变量中心:CGPoint {返回 CGPoint(x: bounds.midX, y: bounds.midY)}}

为了产生上述旋转,我用 viewWillAppear

替换了 viewDidLoad 函数

 override func viewWillAppear(_animated: Bool) {super.viewWillAppear(动画)让 center = centerCircle()//创建一个新的 UIView 并将其添加到视图控制器让 circleView = CircleView()circleView.frame = CGRect(x: center.x, y: center.y, width: 0, height: 0)view.addSubview(circleView)}

viewDidLayoutSubviews

 覆盖 func viewDidLayoutSubviews() {super.viewDidLayoutSubviews()让 center = centerCircle()//创建一个新的 UIView 并将其添加到视图控制器让 circleView = CircleView()circleView.frame = CGRect(x: center.x, y: center.y, width: 0, height: 0)view.addSubview(circleView)}

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

解决方案

首先,viewdidload 和 viewwillappear 中的视图布局不正确,而且您明确定义了带有值的框架,这让我感到非常沮丧作为一个新手程序员.特别是在调整标签大小时,它仍然让我烦恼.所以把你的视图和主屏幕的 centerX 和 centerY 放在一起意味着当你旋转时你的视图会停留在那个点.

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

扩展 UIView {func constrainAndCenter(视图中:UIView,withRelativeHeight:CGFloat,withRelativeWidth:CGFloat)->[NSLayoutConstraint] {self.translatesAutoresizingMaskIntoConstraints = false让 x = NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1, constant: 0)让 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)返回 ([x,w,h,y])}}//然后在 viewdidload 或任何地方让 circleView = CircleView()让 circleViewConstraints = circleView.constrainAndCenter(in: view,....)view.addSubview(circleView)NSLayoutConstraint.activate(circleViewConstraints)

这会将它固定在中间.您也可以在界面生成器中进行.

I am trying to calibrate a UIView to stay on centre as it rotates. This follows up a problem identified by the best answers to my previous question. I have since devised code based on one answer proposing to use viewWillAppear or viewDidLayoutSubviews instead of viewDidLoad.

As I am new to Swift I had to revisit the Swift tutorial Implement a Custom Control which describes how to make a subClass of UIView. My code can draw a circle using any one of three functions: viewWillAppear, viewDidLayoutSubviews or viewDidLoad.

However when I render the code, the circle doesn’t always remain on centre as the screen rotates. Below are successive rotations that happen using viewDidLoad or viewWillAppear When the app is launched on a screen in portrait orientation the problem appears immediately after the first rotation irrespective of left or right. If launched in landscape orientation it is possible to miss a problem if one has not tried at least four successive rotations.

Below are successive rotations that happen using viewDidLayoutSubviews where rotation occasionally results in an on-centre circle overlaying an off-centre circle. This can happen after several rotations but one must complete at least four rotations in order to notice a problem.

Here is the code for CircleView - my attempt to make a subclass of 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
    } 
}

and here is the code for 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)
    }
}

And here is code for the UIScreen extension that returns the screen's centre point

import UIKit

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

To produce the above rotations I replaced viewDidLoad function with 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)
    }

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

Can anyone see a problem in my code that might be stopping rotation from working ?

解决方案

First of all, the view's aren't laid out properly in viewdidload and viewwillappear, and you're explicitly defining the frame with values, something I found extremely frustrating as a newish programmer. It still annoys me particularly when it comes to resizing labels. So putting your view and mainscreen's centreX and centreY means your view will stick at that point when you rotate.

Fix this by learning autolayout. It sucks at first but it will eventually make your life easier. Take a look at the method in this extension. I use it when I want to centre a view to another 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)

That will pin it to the middle. You can also do it in interface builder.

这篇关于为什么 UIView 在旋转时不居中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆