if we're in the real pre-commit handler we can't actually add any new fences due to CA restriction - could this mean something here?
我试图找出为什么自定义uibuttons集合不起作用。在我之前的文章中描述的一个版本中,我用Swift 3编程创建了一个uibuttons循环,并将该循环固定到屏幕的中心。该版本使用了uiview的一个子类——基于AppleSwift教程(实现按钮操作)——以及利用imanou-petit的优秀代码示例(6号锚)实现的自动布局。在那个版本中,当iPhone旋转时,我成功地让我的按钮旋转,但是按钮动作目标无法工作。
所以我现在尝试了一个使用viewcontroller而不是uiview的子类的替代版本。这一次,相同的按钮动作目标可以工作,但旋转手机会使图像偏离中心,如下所示。
每次旋转时,以下消息也会在Xcode的调试区域显示两次。
1 2 | ***[App] if we're in the real pre-commit handler we can't actually add any new fences due to CA restriction*** |
这条信息发生了四次中的三次,也就是说,当手机倒转时没有信息。当我运行上一篇文章中的代码或下面显示的代码时,就会发生这种情况。在每种情况下,颠倒的盒子是被选中还是未被选中都没有区别。
我还尝试禁用操作系统活动模式,但是除了隐藏一条可能解释问题的消息外,没有任何改变。希望比我经验丰富的人能够认识到这个调试消息在我以前的代码(此处显示)或我最新的代码(如下所示)的上下文中意味着什么。
原始代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | import UIKit class ViewController: UIViewController { // MARK: Initialization let points: Int = 10 // 80 25 16 10 5 let dotSize: CGFloat = 60 // 12 35 50 60 99 let radius: CGFloat = 48 // 72 70 64 48 42 var centre: CGPoint? var arcPoint = CGFloat(M_PI * -0.5) // clockwise from 12+ (not 3+)! override func viewDidLoad() { super.viewDidLoad() let myView = UIView() myView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(myView) centre = centrePoint() let horizontalConstraint = myView.centerXAnchor.constraint(equalTo: view.centerXAnchor) let verticalConstraint = myView.centerYAnchor.constraint(equalTo: view.centerYAnchor) NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint]) drawUberCircle() drawBoundaryCircles() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } func drawUberCircle() { // Create a CAShapeLayer let shapeLayer = CAShapeLayer() // give Bezier path layer properties shapeLayer.path = createBezierPath().cgPath // apply layer properties shapeLayer.strokeColor = UIColor.cyan.cgColor shapeLayer.fillColor = UIColor.cyan.cgColor shapeLayer.lineWidth = 1.0 // add layer view.layer.addSublayer(shapeLayer) } func createBezierPath() -> UIBezierPath { // create a new path let path = UIBezierPath(arcCenter: centre!, radius: radius * 2.0, startAngle: CGFloat(M_PI * -0.5), endAngle: CGFloat(M_PI * 1.5), clockwise: true) return path } func drawBoundaryCircles() { for index in 1...points { let point: CGPoint = makeBoundaryPoint(centre: centre!) drawButton(point: point, index: index) } } func makeBoundaryPoint(centre: CGPoint) -> (CGPoint) { arcPoint += arcAngle() print(arcPoint) let point = CGPoint(x: centre.x + (radius * 2 * cos(arcPoint)), y: centre.y + (radius * 2 * sin(arcPoint))) return (point) } func arcAngle() -> CGFloat { return CGFloat(2.0 * M_PI) / CGFloat(points) } func centrePoint() -> CGPoint { return CGPoint(x: view.bounds.midX, y: view.bounds.midY) } func drawButton(point: CGPoint, index: Int) { let myButton = UIButton(type: .custom) as UIButton myButton.frame = CGRect(x: point.x - (dotSize/2), y: point.y - (dotSize/2), width: dotSize, height: dotSize) myButton.backgroundColor = UIColor.white myButton.layer.cornerRadius = dotSize / 2 myButton.layer.borderWidth = 1 myButton.layer.borderColor = UIColor.black.cgColor myButton.clipsToBounds = true myButton.titleLabel!.font = UIFont(name:"HelveticaNeue-Thin", size: dotSize/2) myButton.setTitleColor(UIColor.red, for: .normal) myButton.setTitle(String(index), for: .normal) myButton.tag = index; myButton.sendActions(for: .touchUpInside) myButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside) view.addSubview(myButton) } func buttonAction(myButton: UIButton) { let sender:UIButton = myButton print("Button \(sender.tag) works") } } |
我仍在学习Swift,所以在这个阶段,解决方案是否使用ViewController或uiView的子类并不重要,只要我可以安排一圈ui按钮,这些按钮在我使用自动布局配置它们之后仍然有效。欢迎提出任何建议。谢谢。
解决方案
出现在xcode调试区域的消息——我在本文的主题行中使用的消息——显然不是问题所在。多亏了Rob Mayoff,nslayoutConstraint现在计算每个按钮的尺寸和位置,而这些都是在我的原始代码中运行时之前计算出来的。他的解决方案以及其他一些改进现在反映在下面的代码中。为此,我添加了按钮的原始操作目标。这些功能不仅可以工作,而且只要设备方向发生变化,它们都会锁定在视图的中心。
通过更改半径、buttoncount和buttonsidelength的值,可以很容易地使代码适用于不同的大小配置(参见表)。
这是密码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() createUberCircle() createButtons() } override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .all } private let radius: CGFloat = 85 private let buttonCount = 5 private let buttonSideLength: CGFloat = 100 private func createUberCircle() { let circle = ShapeView() circle.translatesAutoresizingMaskIntoConstraints = false circle.shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: -radius, y: -radius, width: 2*radius, height: 2*radius)).cgPath if buttonCount < 10 { circle.shapeLayer.fillColor = UIColor.clear.cgColor } else { circle.shapeLayer.fillColor = UIColor.cyan.cgColor } view.addSubview(circle) circle.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true circle.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true } private func createButtons() { for i in 1 ... buttonCount { createButton(number: i) } } private func createButton(number: Int) { let button = UIButton(type: .custom) button.translatesAutoresizingMaskIntoConstraints = false button.backgroundColor = .white button.layer.cornerRadius = buttonSideLength / 2 button.layer.borderWidth = 1 button.layer.borderColor = UIColor.black.cgColor button.clipsToBounds = true button.titleLabel!.font = UIFont.systemFont(ofSize: buttonSideLength / 2) if buttonCount > 25 { button.setTitleColor(.clear, for: .normal) } else { button.setTitleColor(.red, for: .normal) } button.setTitle(String(number), for: .normal) button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside) button.tag = number view.addSubview(button) let radians = 2 * CGFloat.pi * CGFloat(number) / CGFloat(buttonCount) - CGFloat.pi / 2 let xOffset = radius * cos(radians) let yOffset = radius * sin(radians) NSLayoutConstraint.activate([ button.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: xOffset), button.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: yOffset), button.widthAnchor.constraint(equalToConstant: buttonSideLength), button.heightAnchor.constraint(equalToConstant: buttonSideLength) ]) } func buttonAction(myButton: UIButton) { let sender:UIButton = myButton print("Button \(sender.tag) works") } } class ShapeView: UIView { override class var layerClass: Swift.AnyClass { return CAShapeLayer.self } lazy var shapeLayer: CAShapeLayer = { self.layer as! CAShapeLayer }() } |
别担心围栏警告信息。它看起来是无害的,不是由你所做的任何事情引起的。
您发布的代码有几个问题:
创建
你在用一个裸露的形状层绘制你的"超级圆圈"。我所说的"裸"是指没有一个视图的
您可以根据顶级视图边界的中心来计算每个按钮的位置。您在
您没有在按钮上设置任何约束,也没有设置它们的自动调整大小蒙版。结果是,当设备旋转时,顶级视图会调整大小,但每个按钮的位置(相对于顶级视图的左上角)保持不变。
打开"颠倒"复选框不足以在iPhone上允许颠倒方向,仅在iPad上。
以下是您需要做的更改:
使用视图绘制"UberCircle"。如果要使用形状层,请创建一个子类
设置从UberCircle中心到顶级视图中心的约束,以便在顶级视图更改大小时保持UberCircle居中。
对于每个按钮,设置从按钮中心到顶层视图中心的约束,以在顶层视图更改大小时保持按钮位置正确。这些约束需要非零常量来从中心偏移按钮。
覆盖
去掉
去掉
因此:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() createUberCircle() createButtons() } override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .all } private let radius: CGFloat = 96 private let buttonCount = 10 private let buttonSideLength: CGFloat = 60 private func createUberCircle() { let circle = ShapeView() circle.translatesAutoresizingMaskIntoConstraints = false circle.shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: -radius, y: -radius, width: 2*radius, height: 2*radius)).cgPath circle.shapeLayer.fillColor = UIColor.cyan.cgColor view.addSubview(circle) circle.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true circle.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true } private func createButtons() { for i in 1 ... buttonCount { createButton(number: i) } } private func createButton(number: Int) { let button = UIButton(type: .custom) button.translatesAutoresizingMaskIntoConstraints = false button.backgroundColor = .white button.layer.cornerRadius = buttonSideLength / 2 button.layer.borderWidth = 1 button.layer.borderColor = UIColor.black.cgColor button.clipsToBounds = true button.titleLabel!.font = UIFont.systemFont(ofSize: buttonSideLength / 2) button.setTitleColor(.red, for: .normal) button.setTitle(String(number), for: .normal) view.addSubview(button) let radians = 2 * CGFloat.pi * CGFloat(number) / CGFloat(buttonCount) - CGFloat.pi / 2 let xOffset = radius * cos(radians) let yOffset = radius * sin(radians) NSLayoutConstraint.activate([ button.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: xOffset), button.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: yOffset), button.widthAnchor.constraint(equalToConstant: buttonSideLength), button.heightAnchor.constraint(equalToConstant: buttonSideLength) ]) } } class ShapeView: UIView { override class var layerClass: Swift.AnyClass { return CAShapeLayer.self } lazy var shapeLayer: CAShapeLayer = { self.layer as! CAShapeLayer }() } |
我将约束父uiview,并将所有按钮约束到父视图的中心,并覆盖layoutSubview。在布局子视图中,可以操作按钮的CenterX和CenterY约束的常量来重新定位它们。或者,您可以将所有按钮居中,然后使用每个按钮的.Transform属性将它们移动到适当的位置。