如果为目标视图指定了初始帧,为什么AutoLayout不遵守约束?

问题描述 投票:0回答:1

[我们有一个奇怪的情况,我们的视图没有按预期的方式出现,我们将其追溯到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,然后它执行与闭包中完全相同的代码。除了赋值之外,闭包和外部代码之间没有任何内容,但是它给出了不同的结果!但是为什么呢?

谁能解释为什么我们看到这种行为?我们缺少什么?

swift xcode autolayout closures swift-playground
1个回答
0
投票

我会说这是因为操场是魔鬼的作品。我在real iOS应用项目中运行了您的代码,即使[],它也能按预期工作

SomeView(frame:CGRect(x: 0, y: 0, width: 20, height: 20))

基本上,我不希望游乐场能够正确地通过视图控制器的所有生命周期阶段。

© www.soinside.com 2019 - 2024. All rights reserved.