问题描述
我试图找出为什么自定义 UIButtons 集合不起作用.在
每次旋转时,以下消息也会在 Xcode 的调试区域中出现两次.
***[App] 如果我们在真正的预提交处理程序中,我们不能由于 CA 限制,实际上添加了任何新的栅栏***
消息四次出现三次,即当手机倒置时没有消息.当我在我的以前的帖子 或代码如下所示.在每种情况下,选中或取消选中上下颠倒框都没有区别.
我也试过
这里是代码
导入 UIKit类视图控制器:UIViewController {覆盖 func viewDidLoad() {super.viewDidLoad()创建UberCircle()创建按钮()}覆盖 var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .all }私人让半径:CGFloat = 85私人让 buttonCount = 5私有让 buttonSideLength: CGFloat = 100私人 func createUberCircle() {让圆圈 = ShapeView()circle.translatesAutoresizingMaskIntoConstraints = falsecircle.shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: -radius, y: -radius, width: 2*radius, height: 2*radius)).cgPath如果按钮计数 <10 {circle.shapeLayer.fillColor = UIColor.clear.cgColor} 别的 {circle.shapeLayer.fillColor = UIColor.cyan.cgColor}view.addSubview(圈子)circle.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = truecircle.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true}私有函数 createButtons() {对于我在 1 ... buttonCount {创建按钮(编号:i)}}私人 func createButton(number: Int) {让按钮= UIButton(类型:.custom)button.translatesAutoresizingMaskIntoConstraints = falsebutton.backgroundColor = .whitebutton.layer.cornerRadius = buttonSideLength/2button.layer.borderWidth = 1button.layer.borderColor = UIColor.black.cgColorbutton.clipsToBounds = truebutton.titleLabel!.font = UIFont.systemFont(ofSize: buttonSideLength/2)如果按钮计数 >25 {button.setTitleColor(.clear, for: .normal)} 别的 {button.setTitleColor(.red, for: .normal)}button.setTitle(String(number), for: .normal)button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)button.tag = 数字view.addSubview(按钮)让弧度 = 2 * CGFloat.pi * CGFloat(number)/CGFloat(buttonCount) - CGFloat.pi/2让 xOffset = 半径 * cos(弧度)让 yOffset = 半径 * sin(弧度)NSLayoutConstraint.activate([button.centerXAnchor.constraint(equalTo: view.centerXAnchor, 常量: xOffset),button.centerYAnchor.constraint(equalTo: view.centerYAnchor, 常量: yOffset),button.widthAnchor.constraint(equalToConstant: buttonSideLength),button.heightAnchor.constraint(equalToConstant: buttonSideLength)])}func buttonAction(myButton: UIButton) {让发件人:UIButton = myButtonprint("按钮 (sender.tag) 有效")}}类ShapeView:UIView {覆盖类 var layerClass: Swift.AnyClass { return CAShapeLayer.self }懒惰的 var shapeLayer: CAShapeLayer = { self.layer as!CAShapeLayer }()}
不用担心栅栏警告信息.它似乎是无害的,不是由您正在做的任何事情引起的.
您发布的代码有几个问题:
您创建
myView
并限制其中心,但您不给它任何大小限制.此外,myView
是一个局部变量,您不向myView
添加任何子视图.所以myView
是一个不可见的、无大小的视图,没有内容.你为什么要创建它?您正在使用裸形状图层绘制uberCircle".裸"是指没有视图的
layer
属性是该层.裸层不参与自动布局.您根据顶层视图边界的中心计算每个按钮的位置.您在
viewDidLoad
期间执行此操作,但在调整顶级视图大小以适应当前设备之前调用了viewDidLoad
.因此,在某些设备上,您的方向盘甚至不会在启动时居中.您没有对按钮设置任何约束,也没有设置它们的自动调整大小掩码.结果是,当设备旋转时,顶层视图会调整大小,但每个按钮的位置(相对于顶层视图的左上角)保持不变.
启用倒置"复选框不足以在 iPhone 上允许倒置方向,仅在 iPad 上.
以下是您需要进行的更改:
使用视图绘制uberCircle".如果要使用形状层,请创建
UIView
的子类,该子类使用CAShapeLayer
作为其层.您可以从 this answer 复制ShapeView
类.设置从 uberCircle 中心到顶层视图中心的约束,以在顶层视图改变大小时保持 uberCircle 居中.
对于每个按钮,设置从按钮中心到顶层视图中心的约束,以在顶层视图改变大小时保持按钮正确定位.这些约束需要非零常量来使按钮偏离中心.
覆盖
supportedInterfaceOrientations
以启用倒置方向(除了选中倒置"复选框之外).去掉
viewDidLoad
中的myView
.你不需要它.去掉
centre
属性.你不需要它.
因此:
导入 UIKit类视图控制器:UIViewController {覆盖 func viewDidLoad() {super.viewDidLoad()创建UberCircle()创建按钮()}覆盖 var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .all }私人让半径:CGFloat = 96私人让 buttonCount = 10私有让 buttonSideLength: CGFloat = 60私人 func createUberCircle() {让圆圈 = ShapeView()circle.translatesAutoresizingMaskIntoConstraints = falsecircle.shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: -radius, y: -radius, width: 2*radius, height: 2*radius)).cgPathcircle.shapeLayer.fillColor = UIColor.cyan.cgColorview.addSubview(圈子)circle.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = truecircle.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true}私有函数 createButtons() {对于我在 1 ... buttonCount {创建按钮(编号:i)}}私人 func createButton(number: Int) {让按钮= UIButton(类型:.custom)button.translatesAutoresizingMaskIntoConstraints = falsebutton.backgroundColor = .whitebutton.layer.cornerRadius = buttonSideLength/2button.layer.borderWidth = 1button.layer.borderColor = UIColor.black.cgColorbutton.clipsToBounds = truebutton.titleLabel!.font = UIFont.systemFont(ofSize: buttonSideLength/2)button.setTitleColor(.red, for: .normal)button.setTitle(String(number), for: .normal)view.addSubview(按钮)让弧度 = 2 * CGFloat.pi * CGFloat(number)/CGFloat(buttonCount) - CGFloat.pi/2让 xOffset = 半径 * cos(弧度)让 yOffset = 半径 * sin(弧度)NSLayoutConstraint.activate([button.centerXAnchor.constraint(equalTo: view.centerXAnchor, 常量: xOffset),button.centerYAnchor.constraint(equalTo: view.centerYAnchor, 常量: yOffset),button.widthAnchor.constraint(equalToConstant: buttonSideLength),button.heightAnchor.constraint(equalToConstant: buttonSideLength)])}}类ShapeView:UIView {覆盖类 var layerClass: Swift.AnyClass { return CAShapeLayer.self }懒惰的 var shapeLayer: CAShapeLayer = { self.layer as!CAShapeLayer }()}
I am trying to find why a collection of custom UIButtons does not work. In a version described in my earlier post I created a circle of UIButtons programmatically in Swift 3 and anchored the circle to the centre of the screen. That version used a subclass of UIView - based on an Apple Swift tutorial (Implement the Button Action) - together with an implementation of autolayout that draws on Imanou Petit’s excellent code examples (No.6 Anchor). In that version I managed to get my buttons to rotate successfully when the iPhone rotates but the button action-target fails to work.
So I have now tried an alternative version using a viewcontroller instead of a subclass of UIView. This time the same button action-target works but rotating the phone causes the image to shift away from the centre as shown below.
With each rotation the following message also appears twice in the debug area of Xcode.
***[App] if we're in the real pre-commit handler we can't
actually add any new fences due to CA restriction***
The message happens three times out of four, i.e. there is no message when the phone is turned upside down. This occurs when I run either the code in my previous post or the code shown below. And in each case it made no difference whether the Upside Down box was checked or un-checked.
I also tried disabling OS_ACTIVITY MODE but that changed nothing except hide a message that might potentially explain the problem. Someone more experienced than me will hopefully recognise what this debug message means either in the context of my previous code (shown here) or my latest code, shown below.
ORIGINAL CODE
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")
}
}
I am still in the process of learning Swift so it doesn’t matter at this stage whether the solution uses a viewcontroller or a subclass of UIView so long as I can arrange a circle of UIButtons that still work after I configure them using autolayout. Every suggestion is welcome. Thanks.
SOLUTION
The message that appeared in Xcode’s debug area - and which I used in the subject line of this post - was clearly not the issue. Thanks to Rob Mayoff, NSLayoutConstraint now computes the dimensions and position of each button whereas these were computed prior to run-time in my original code. His solution along with several other improvements are now reflected in the code below. To this I added the original action-target for the buttons. These not only work but remain locked to the centre of the view whenever the device orientation changes.
The code can easily be made to work for a different size configuration by changing values for radius, buttonCount and buttonSideLength (see table).
Here is the code
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 }()
}
Don't worry about the fences warning message. It seems to be harmless and not caused by anything you're doing.
There are several problems with the code you posted:
You create
myView
and constrain its center, but you don't give it any size constraints. Furthermore,myView
is a local variable and you don't add any subviews tomyView
. SomyView
is an invisible, sizeless view with no contents. Why are you creating it at all?You're drawing your "uberCircle" using a bare shape layer. By "bare", I mean there's no view whose
layer
property is that layer. Bare layers don't participate in autolayout.You compute the position of each button based on the center of the top-level view's bounds. You're doing this during
viewDidLoad
, butviewDidLoad
is called before the top-level view has been resized to fit the current device. So your wheel won't even be centered at launch on some devices.You don't set any constraints on the buttons, or set their autoresizing masks. The result is that, when the device rotates, the top-level view resizes but each button's position (relative to the top-left corner of the top-level view) stays the same.
Turning on the "Upside Down" checkbox is not sufficient to allow upside-down orientation on iPhones, only on iPads.
Here are the changes you need to make:
Use a view to draw the "uberCircle". If you want to use a shape layer, make a subclass of
UIView
that uses aCAShapeLayer
for its layer. You can copy theShapeView
class from this answer.Set constraints from the center of the uberCircle to the center of the top-level view, to keep the uberCircle centered when the top-level view changes size.
For each button, set constraints from the center of the button to the center of the top-level view, to keep the button positioned properly when the top-level view changes size. These constraints need non-zero constants to offset the buttons from the center.
Override
supportedInterfaceOrientations
to enable upside-down orientation (in addition to checking the "Upside Down" checkbox).Get rid of
myView
inviewDidLoad
. You don't need it.Get rid of the
centre
property. You don't need it.
Thus:
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 }()
}
这篇关于如果我们在真正的预提交处理程序中,由于 CA 限制,我们实际上不能添加任何新的栅栏——这可能意味着什么吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!