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

Gre*_*reg 5 iphone uibutton uiview autolayout swift

我试图找到为什么自定义UIButton集合不起作用.在我之前的文章中描述的版本中,我Swift 3中以编程方式创建了一个UIButton圆圈,并将圆圈固定在屏幕的中心.该版本使用了UIView的子类 - 基于Apple Swift教程(实现Button Action) - 以及利用Imanou Petit优秀代码示例(No.6 Anchor)的autolayout实现.在那个版本中,我设法让我的按钮在iPhone旋转时成功旋转,但按钮动作目标无法工作.

所以我现在尝试使用viewcontroller而不是UIView的子类的替代版本.这次相同的按钮动作目标有效,但旋转手机会使图像偏离中心,如下所示.

在此输入图像描述

每次旋转时,以下消息也会在Xcode的调试区域中出现两次.

    ***[App] if we're in the real pre-commit handler we can't 
    actually add any new fences due to CA restriction***
Run Code Online (Sandbox Code Playgroud)

该消息在四个中发生三次,即当手机上下颠倒时没有消息.当我运行上一篇文章中的代码或下面显示代码时,会发生这种情况.并且在每种情况下,"倒置"框是否已选中或未选中都没有区别.

在此输入图像描述 我也试过禁用OS_ACTIVITY MODE但除了隐藏可能解释问题的消息之外什么都没改变.更有经验的人比我有希望认识一下这个调试消息无论是在我以前的代码(上下文意思此处所示)或我最新的代码,如下图所示.

原始代码

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")
    }
}
Run Code Online (Sandbox Code Playgroud)

我仍然在学习Swift,所以在这个阶段,解决方案是使用视图控制器还是UIView的子类无关紧要,只要我可以在使用autolayout配置它们之后安排一圈UIButton仍然有效.欢迎提出任何建议.谢谢.

在Xcode的调试区域中出现的消息 - 我在本文的主题行中使用的消息 - 显然不是问题.感谢Rob Mayoff,NSLayoutConstraint现在计算每个按钮的尺寸和位置,而这些是在我的原始代码中在运行时之前计算的.他的解决方案以及其他一些改进现在反映在下面的代码中.为此我添加了按钮的原始动作目标.这些不仅可以工作,而且只要设备方向发生变化,就会保持锁定在视图的中心.

通过更改radius,buttonCount和buttonSideLength的值,可以轻松地使代码适用于不同大小的配置(请参阅表格).

在此输入图像描述

这是代码

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 }()
    }
Run Code Online (Sandbox Code Playgroud)

rob*_*off 5

不要担心围栏警告信息.它似乎是无害的,不是你正在做的任何事情造成的.

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

  1. 您可以创建myView和约束其中心,但不要给它任何大小限制.此外,myView是一个局部变量,您不添加任何子视图myView.所以myView是一个看不见的,无大小的视图,没有内容.你为什么要创造它?

  2. 你正在使用裸形图层绘制你的"uberCircle".通过"裸",我的意思是没有视图,其layer属性是该层.裸层不参与自动布局.

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

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

  5. 打开"颠倒"复选框不足以允许iPhone上的倒置方向,仅适用于iPad.

以下是您需要进行的更改:

  1. 使用视图绘制"uberCircle".如果要使用形状图层,请创建UIView一个CAShapeLayer用于其图层的子类.您可以ShapeView从此答案中复制该课程.

  2. 设置从uberCircle中心到顶层视图中心的约束,以便在顶层视图更改大小时保持uberCircle居中.

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

  4. 覆盖supportedInterfaceOrientations以启用颠倒方向(除了选中"颠倒"复选框).

  5. 摆脱myViewviewDidLoad.你不需要它.

  6. 摆脱centre财产.你不需要它.

从而:

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

}
Run Code Online (Sandbox Code Playgroud)