关于iphone:如果我们在真正的预提交处理程序中,由于CA限制,我们实际上无法添加任何新的围栏 – 这可能意味着什么吗?

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的子类的替代版本。这一次,相同的按钮动作目标可以工作,但旋转手机会使图像偏离中心,如下所示。

enter image description here

每次旋转时,以下消息也会在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***

这条信息发生了四次中的三次,也就是说,当手机倒转时没有信息。当我运行上一篇文章中的代码或下面显示的代码时,就会发生这种情况。在每种情况下,颠倒的盒子是被选中还是未被选中都没有区别。

enter image description here我还尝试禁用操作系统活动模式,但是除了隐藏一条可能解释问题的消息外,没有任何改变。希望比我经验丰富的人能够认识到这个调试消息在我以前的代码(此处显示)或我最新的代码(如下所示)的上下文中意味着什么。

原始代码

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的值,可以很容易地使代码适用于不同的大小配置(参见表)。

enter image description here

这是密码

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


别担心围栏警告信息。它看起来是无害的,不是由你所做的任何事情引起的。

您发布的代码有几个问题:

  • 创建myView并约束其中心,但不给它任何大小约束。此外,myView是一个局部变量,您不需要向myView添加任何子视图。因此,myView是一个不可见的、没有大小的视图,没有内容。你为什么要创造它?

  • 你在用一个裸露的形状层绘制你的"超级圆圈"。我所说的"裸"是指没有一个视图的layer属性是该层。裸露层不参与自动布局。

  • 您可以根据顶级视图边界的中心来计算每个按钮的位置。您在viewDidLoad期间执行此操作,但在调整顶层视图的大小以适应当前设备之前调用viewDidLoad。因此,在某些设备上,您的车轮甚至不会在发射时居中。

  • 您没有在按钮上设置任何约束,也没有设置它们的自动调整大小蒙版。结果是,当设备旋转时,顶级视图会调整大小,但每个按钮的位置(相对于顶级视图的左上角)保持不变。

  • 打开"颠倒"复选框不足以在iPhone上允许颠倒方向,仅在iPad上。

  • 以下是您需要做的更改:

  • 使用视图绘制"UberCircle"。如果要使用形状层,请创建一个子类UIView,该子类使用CAShapeLayer作为其层。您可以从这个答案中复制ShapeView类。

  • 设置从UberCircle中心到顶级视图中心的约束,以便在顶级视图更改大小时保持UberCircle居中。

  • 对于每个按钮,设置从按钮中心到顶层视图中心的约束,以在顶层视图更改大小时保持按钮位置正确。这些约束需要非零常量来从中心偏移按钮。

  • 覆盖supportedInterfaceOrientations以启用上下方向(除了选中"上下"复选框)。

  • 去掉viewDidLoad中的myView。你不需要它。

  • 去掉centre的财产。你不需要它。

  • 因此:

    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属性将它们移动到适当的位置。