[我们有一个奇怪的情况,我们的视图没有按预期的方式出现,我们将其追溯到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()~>{
$0.backgroundColor = .blue
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
NSLayoutConstraint.activate([
$0.leftAnchor.constraint(equalTo: view.leftAnchor, constant: margin),
$0.topAnchor.constraint(equalTo: view.topAnchor, constant: margin),
$0.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -margin),
$0.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))~>{
$0.backgroundColor = .blue
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
NSLayoutConstraint.activate([
$0.leftAnchor.constraint(equalTo: view.leftAnchor, constant: margin),
$0.topAnchor.constraint(equalTo: view.topAnchor, constant: margin),
$0.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -margin),
$0.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))~>{
$0.backgroundColor = .blue
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
}
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)
])
}
[让我们感到困惑的是,在先前的代码中,闭包内的$0
是对新创建的视图的强大引用(并且当父视图作为子视图添加时,父级也保留了对它的引用,因此它不会卸载),并将完全相同的引用分配给外部的someView
,然后它执行与闭包中完全相同的代码。除了赋值之外,闭包和外部代码之间没有任何内容,但是它给出了不同的结果!但是为什么呢?
谁能解释为什么我们看到这种行为?我们缺少什么?
我会说这是因为操场是魔鬼的作品。我在real iOS应用项目中运行了您的代码,即使[],它也能按预期工作
SomeView(frame:CGRect(x: 0, y: 0, width: 20, height: 20))
基本上,我不希望游乐场能够正确地通过视图控制器的所有生命周期阶段。