Если мы находимся в реальном обработчике pre-commit, мы фактически не можем добавлять какие-либо новые заборы из-за ограничения CA - может это что-то значит здесь?
Я пытаюсь найти, почему коллекция пользовательских UIButtons не работает. В версии описанной в моем предыдущем сообщении, я создал круг UIButtons programmatically в Swift 3 и привязали круг к центру экрана. В этой версии был использован подкласс UIView - на основе учебника Apple Swift (реализация действия кнопки) - вместе с реализацией автозапуска, которая опирается на Imanou Petits отлично примеры кода (No.6 Anchor). В этой версии мне удалось заставить мои кнопки успешно вращаться, когда 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***
Сообщение происходит три раза из четырех, т.е. нет сообщения, когда телефон перевернут вверх дном. Это происходит, когда я запускаю код в своем предыдущем сообщении или код, показанный ниже. И в каждом случае не имело значения, было ли проверено или не проверено поле Upside Down.
Я также попробовал отключить 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")
}
}
Я все еще участвую в процессе обучения Swift, поэтому на данном этапе не имеет значения, использует ли решение viewcontroller или подкласс UIView, пока я могу организовать круг UIButtons, который все еще работает после того, как я настроил их с помощью autolayout. Каждое предложение приветствуется. Спасибо.
Решение
Сообщение, появившееся в области отладки Xcodes, и которое я использовал в строке темы этого сообщения, явно не было проблемой. Благодаря Rob Mayoff NSLayoutConstraint теперь вычисляет размеры и положение каждой кнопки, тогда как они были вычислены до времени выполнения в моем исходном коде. Его решение наряду с несколькими другими улучшениями теперь отражено в коде ниже. К этому я добавил исходную целевую кнопку для кнопок. Они не только работают, но и остаются заблокированными в центре просмотра всякий раз, когда меняется ориентация устройства.
Код может быть легко выполнен для работы с другой конфигурацией размера, изменяя значения радиуса, 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 }()
}
Ответы
Ответ 1
Не беспокойтесь о предупреждении о заборе. Это кажется безобидным и не вызванным чем-либо, что вы делаете.
Есть несколько проблем с кодом, который вы опубликовали:
-
Вы создаете myView
и ограничиваете его центр, но вы не даете ему никаких ограничений по размеру. Кроме того, myView
является локальной переменной, и вы не добавляете никаких подзонов в myView
. Таким образом, myView
является невидимым, безболезненным представлением без содержимого. Почему вы его вообще создаете?
-
Вы рисуете свой "uberCircle", используя слой с голой фигурой. "Голый", я имею в виду, что нет представления, чье свойство layer
является этим слоем. Голые слои не участвуют в автозапуске.
-
Вы вычисляете положение каждой кнопки в зависимости от центра границ верхнего уровня. Вы делаете это во время viewDidLoad
, но viewDidLoad
вызывается до того, как вид верхнего уровня был изменен, чтобы соответствовать текущему устройству. Таким образом, ваше колесо даже не будет сосредоточено при запуске на некоторых устройствах.
-
Вы не устанавливаете никаких ограничений на кнопки или не устанавливаете свои маски авторезистировки. В результате, когда устройство вращается, вид верхнего уровня изменяется, но положение каждой кнопки (относительно верхнего левого угла верхнего уровня) остается прежним.
-
Включение флажка "Upside Down" недостаточно, чтобы разрешить перевернутую ориентацию на iPhone, только на iPads.
Вот изменения, которые вам нужно сделать:
-
Используйте вид, чтобы нарисовать "uberCircle". Если вы хотите использовать слой формы, создайте подкласс UIView
, который использует CAShapeLayer
для своего слоя. Вы можете скопировать класс ShapeView
из этого ответа.
-
Задайте ограничения из центра uberCircle в центр верхнего уровня, чтобы поддерживать uberCircle в центре, когда вид верхнего уровня меняет размер.
-
Для каждой кнопки задайте ограничения от центра кнопки до середины верхнего уровня, чтобы держать кнопку правильно, когда вид верхнего уровня меняет размер. Эти ограничения нуждаются в ненулевых константах для смещения кнопок от центра.
-
Переопределить supportedInterfaceOrientations
, чтобы включить перевернутую ориентацию (в дополнение к проверке флажка "вверх дном" ).
-
Избавиться от myView
в viewDidLoad
. Вам это не нужно.
-
Избавьтесь от свойства 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 }()
}
Ответ 2
Я бы просто ограничил родительский UIView и сдерживал все кнопки в центре родительского представления и переопределял layoutSubviews. В представлении макета вы можете манипулировать константами ограничений центра X и centerY, чтобы кнопки могли их переместить. В качестве альтернативы вы можете просто сосредоточить все их и использовать свойство .transform каждой кнопки, чтобы переместить их на место.