如果为目标视图指定了初始框架,为什么 AutoLayout 不遵守约束?
Why would AutoLayout not respect constraints if an initial frame is specified for the target view?
我们有一个奇怪的情况,我们的视图没有按预期显示,我们已经将其追溯到 AutoLayout 和一些我们看不出为什么它会改变事情的事件序列。
为简化示例,这里有一些测试代码,您可以直接在 Xcode 游乐场中 运行。 (如果重要的话,我们使用 Xcode 11.3.1。)
首先,我们定义一个InlineConfigure
运算符,像这样...
// 'InlineConfigure' operator
infix operator ~> : AssignmentPrecedence
@discardableResult
public func ~> <T>(item:T, _ configure:(T)->Void) -> T {
configure(item)
return item
}
接下来,为了说明这不是在不期望的情况下释放某些东西的情况,我们创建了一个 UIView
子类专门用于记录...
class SomeView : UIView {
override init(frame: CGRect) {
super.init(frame:frame)
print("SomeView created")
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
print("SomeView deinitialized")
}
}
以上两点都准备就绪后,请看一下这段代码。它按预期工作。
class TestViewController : UIViewController {
let margin:CGFloat = 24
override func loadView() {
view = UIView()
view.backgroundColor = .yellow
SomeView()~>{
[=12=].backgroundColor = .blue
[=12=].translatesAutoresizingMaskIntoConstraints = false
view.addSubview([=12=])
NSLayoutConstraint.activate([
[=12=].leftAnchor.constraint(equalTo: view.leftAnchor, constant: margin),
[=12=].topAnchor.constraint(equalTo: view.topAnchor, constant: margin),
[=12=].rightAnchor.constraint(equalTo: view.rightAnchor, constant: -margin),
[=12=].bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -margin)
])
}
}
}
PlaygroundPage.current.liveView = TestViewController()
但是,如果我们更改创建 SomeView
的行以采用具有非零区域(例如,宽度和高度)的框架,那么相同的代码将不再有效,您也不会完全看不到蓝景。
override func loadView() {
view = UIView()
view.backgroundColor = .yellow
SomeView(frame:CGRect(x: 0, y: 0, width: 20, height: 20))~>{
[=13=].backgroundColor = .blue
[=13=].translatesAutoresizingMaskIntoConstraints = false
view.addSubview([=13=])
NSLayoutConstraint.activate([
[=13=].leftAnchor.constraint(equalTo: view.leftAnchor, constant: margin),
[=13=].topAnchor.constraint(equalTo: view.topAnchor, constant: margin),
[=13=].rightAnchor.constraint(equalTo: view.rightAnchor, constant: -margin),
[=13=].bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -margin)
])
}
}
将框架设置为零区域,它再次起作用...
SomeView(frame:CGRect(x: 0, y: 0, width: 0, height: 20))~>{
[...]
}
但是回到非零框架,如果将约束设置代码移到配置闭包之外,那么无论框架是否有区域,它总是有效的!
override func loadView() {
view = UIView()
view.backgroundColor = .yellow
let someView = SomeView(frame:CGRect(x: 0, y: 0, width: 20, height: 20))~>{
[=15=].backgroundColor = .blue
[=15=].translatesAutoresizingMaskIntoConstraints = false
view.addSubview([=15=])
}
NSLayoutConstraint.activate([
someView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: margin),
someView.topAnchor.constraint(equalTo: view.topAnchor, constant: margin),
someView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -margin),
someView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -margin)
])
}
让我们感到困惑的是在前面的代码中,[=19=]
闭包内部是对新创建视图的强引用(当它作为子视图添加时,父级也持有对它的引用所以它没有卸载),并且在外部将完全相同的引用分配给 someView
然后它执行与闭包中的代码完全相同的代码。除了赋值外,闭包和外部代码之间没有任何东西,但它给出了不同的结果!但是为什么?
谁能解释为什么我们会看到这种行为?我们缺少什么?
我会说这是因为游乐场是魔鬼的杰作。我 运行 你的代码在 真实 iOS 应用程序项目中,即使使用
它也能按预期工作
SomeView(frame:CGRect(x: 0, y: 0, width: 20, height: 20))
基本上我不希望 playground 正确地经历视图控制器的所有生命周期阶段。
我们有一个奇怪的情况,我们的视图没有按预期显示,我们已经将其追溯到 AutoLayout 和一些我们看不出为什么它会改变事情的事件序列。
为简化示例,这里有一些测试代码,您可以直接在 Xcode 游乐场中 运行。 (如果重要的话,我们使用 Xcode 11.3.1。)
首先,我们定义一个InlineConfigure
运算符,像这样...
// 'InlineConfigure' operator
infix operator ~> : AssignmentPrecedence
@discardableResult
public func ~> <T>(item:T, _ configure:(T)->Void) -> T {
configure(item)
return item
}
接下来,为了说明这不是在不期望的情况下释放某些东西的情况,我们创建了一个 UIView
子类专门用于记录...
class SomeView : UIView {
override init(frame: CGRect) {
super.init(frame:frame)
print("SomeView created")
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
print("SomeView deinitialized")
}
}
以上两点都准备就绪后,请看一下这段代码。它按预期工作。
class TestViewController : UIViewController {
let margin:CGFloat = 24
override func loadView() {
view = UIView()
view.backgroundColor = .yellow
SomeView()~>{
[=12=].backgroundColor = .blue
[=12=].translatesAutoresizingMaskIntoConstraints = false
view.addSubview([=12=])
NSLayoutConstraint.activate([
[=12=].leftAnchor.constraint(equalTo: view.leftAnchor, constant: margin),
[=12=].topAnchor.constraint(equalTo: view.topAnchor, constant: margin),
[=12=].rightAnchor.constraint(equalTo: view.rightAnchor, constant: -margin),
[=12=].bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -margin)
])
}
}
}
PlaygroundPage.current.liveView = TestViewController()
但是,如果我们更改创建 SomeView
的行以采用具有非零区域(例如,宽度和高度)的框架,那么相同的代码将不再有效,您也不会完全看不到蓝景。
override func loadView() {
view = UIView()
view.backgroundColor = .yellow
SomeView(frame:CGRect(x: 0, y: 0, width: 20, height: 20))~>{
[=13=].backgroundColor = .blue
[=13=].translatesAutoresizingMaskIntoConstraints = false
view.addSubview([=13=])
NSLayoutConstraint.activate([
[=13=].leftAnchor.constraint(equalTo: view.leftAnchor, constant: margin),
[=13=].topAnchor.constraint(equalTo: view.topAnchor, constant: margin),
[=13=].rightAnchor.constraint(equalTo: view.rightAnchor, constant: -margin),
[=13=].bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -margin)
])
}
}
将框架设置为零区域,它再次起作用...
SomeView(frame:CGRect(x: 0, y: 0, width: 0, height: 20))~>{
[...]
}
但是回到非零框架,如果将约束设置代码移到配置闭包之外,那么无论框架是否有区域,它总是有效的!
override func loadView() {
view = UIView()
view.backgroundColor = .yellow
let someView = SomeView(frame:CGRect(x: 0, y: 0, width: 20, height: 20))~>{
[=15=].backgroundColor = .blue
[=15=].translatesAutoresizingMaskIntoConstraints = false
view.addSubview([=15=])
}
NSLayoutConstraint.activate([
someView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: margin),
someView.topAnchor.constraint(equalTo: view.topAnchor, constant: margin),
someView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -margin),
someView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -margin)
])
}
让我们感到困惑的是在前面的代码中,[=19=]
闭包内部是对新创建视图的强引用(当它作为子视图添加时,父级也持有对它的引用所以它没有卸载),并且在外部将完全相同的引用分配给 someView
然后它执行与闭包中的代码完全相同的代码。除了赋值外,闭包和外部代码之间没有任何东西,但它给出了不同的结果!但是为什么?
谁能解释为什么我们会看到这种行为?我们缺少什么?
我会说这是因为游乐场是魔鬼的杰作。我 运行 你的代码在 真实 iOS 应用程序项目中,即使使用
它也能按预期工作SomeView(frame:CGRect(x: 0, y: 0, width: 20, height: 20))
基本上我不希望 playground 正确地经历视图控制器的所有生命周期阶段。