使用 NSLayoutConstraints 初始化器与 Anchor 设置约束之间的区别

sha*_*man 1 anchor autolayout nslayoutconstraint swift

为什么有些开发人员会添加这样的约束:

NSLayoutConstraint(item: myView, attribute: .right, relatedBy: .equal, toItem: view, attribute: .right, multiplier: 1.0, constant: 20.0).isActive = true
Run Code Online (Sandbox Code Playgroud)

还有一些像这样:

myView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 20).isActive = true
Run Code Online (Sandbox Code Playgroud)

他们基本上做同样的事情......对吧?那么它们之间有什么区别呢?为什么应该使用其中一种而不是另一种?使用其中一种与另一种相比是否存在性能差异?

在我工作的地方,我们的 iOS 主管专门使用NSLayoutConstraint初始化方式,每个人都被迫这样做,以提高整个代码的一致性和可读性,我喜欢这两种方式,我只是想知道是否有任何初始化方式使用其中一种比另一种有什么好处?或者差异只是基于偏好?

Don*_*Mag 6

在很大程度上,它是新的语法和可读性,但仍然有一些你可以用NSLayoutConstraint(...)“新”方式做的事情。

例如,让我们执行一个简单的任务,添加一个UILabel水平居中、距离底部 40 点的元素。以下每个示例都将以此开头:

override func viewDidLoad() {
    super.viewDidLoad()
    
    let myView = UILabel()
    myView.backgroundColor = .green
    myView.text = "Hello"
    
    myView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(myView)
    
    // add constraints....

}
Run Code Online (Sandbox Code Playgroud)

所以,我们的第一个方法如下所示:

        // center horizontally
        NSLayoutConstraint(item: myView,
                           attribute: .centerX,
                           relatedBy: .equal,
                           toItem: view,
                           attribute: .centerX,
                           multiplier: 1.0,
                           constant: 0.0).isActive = true
        
        // bottom = 40-pts from view Bottom
        NSLayoutConstraint(item: myView,
                           attribute: .bottom,
                           relatedBy: .equal,
                           toItem: view,
                           attribute: .bottom,
                           multiplier: 1.0,
                           constant: -40.0).isActive = true
Run Code Online (Sandbox Code Playgroud)

我们可以使用这种语法得到完全相同的结果,通常认为这种语法更“可读”:

        // center horizontally
        myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        // bottom = 40-pts from view Bottom
        myView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -40.0).isActive = true
Run Code Online (Sandbox Code Playgroud)

现在,我们通常需要设置更多约束,因此我们可以使用它使其更具可读性(消除.isActive = true每行末尾的 ):

        NSLayoutConstraint.activate([
            // center horizontally
            myView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            // bottom = 40-pts from view Bottom
            myView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -40.0),
        ])
Run Code Online (Sandbox Code Playgroud)

那么...如果我们添加一点复杂性,例如“使标签底部距视图底部20% ”,会发生什么?


为了坚持“新的、更易读的”语法,我们有几个选择......

1 - 将标签的底部约束到视图的底部,等到布局完成 - 这样我们就知道视图的高度 - 然后将.constant底部锚点设置为-(view height * 0.2)。这可行,但每次标签的超级视图发生变化时(例如在设备旋转时),我们都必须重新计算。

2 - 添加 aUIView作为“底部垫片”:

        // add a hidden UIView for bottom "space"
        let spacerView = UIView()
        spacerView.isHidden = true
        view.addSubview(spacerView)
        spacerView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            // spacerView at bottom, height = 20% of view height
            spacerView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0.0),
            spacerView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.2),

            // center horizontally
            myView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            // bottom = spacerView Top
            myView.bottomAnchor.constraint(equalTo: spacerView.topAnchor, constant: 0.0),
        ])
Run Code Online (Sandbox Code Playgroud)

这可以工作,并且可以处理超级视图大小的更改,但是我们在视图层次结构中添加了另一个视图。对于这个简单的例子来说,没什么大不了的,但我们可能不想为复杂的布局添加一堆它们。

3 - 添加 aUILayoutGuide作为“底部垫片”:

        // add a UILayoutGuide for bottom "space"
        let spacerGuide = UILayoutGuide()
        view.addLayoutGuide(spacerGuide)
        NSLayoutConstraint.activate([
            // spacerGuide at bottom, height = 20% of view height
            spacerGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0.0),
            spacerGuide.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.2),
            
            // center horizontally
            myView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            // bottom = spacerGuide Top
            myView.bottomAnchor.constraint(equalTo: spacerGuide.topAnchor, constant: 0.0),
        ])
Run Code Online (Sandbox Code Playgroud)

完成同样的事情,但现在我们使用非渲染UI 元素,因此我们不会压垮视图层次结构。

4 -使用NSLayoutConstraint(...)语法,并避免所有这些:

        // center horizontally
        NSLayoutConstraint(item: myView,
                           attribute: .centerX,
                           relatedBy: .equal,
                           toItem: view,
                           attribute: .centerX,
                           multiplier: 1.0,
                           constant: 0.0).isActive = true
        
        // bottom = 80% of view bottom (leaves 20% space at bottom)
        NSLayoutConstraint(item: myView,
                           attribute: .bottom,
                           relatedBy: .equal,
                           toItem: view,
                           attribute: .bottom,
                           multiplier: 0.8,
                           constant: 0.0).isActive = true
    }
Run Code Online (Sandbox Code Playgroud)

因此,对于大多数情况,这是一个偏好和/或一致性问题,但您会发现偶尔存在差异