使用 NSLayoutConstraints 初始值设定项与使用 Anchors 设置约束之间的区别
Difference between using NSLayoutConstraints initializer compared to Anchors for setting constraints
为什么有些开发人员会添加这样的约束:
NSLayoutConstraint(item: myView, attribute: .right, relatedBy: .equal, toItem: view, attribute: .right, multiplier: 1.0, constant: 20.0).isActive = true
有些像这样:
myView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 20).isActive = true
他们基本上做同样的事情......对吧?那么它们之间有什么区别呢?为什么应该使用一个而不是另一个?使用一个与另一个相比有性能差异吗?
在我工作的地方,我们的 iOS 领导专门使用 NSLayoutConstraint
初始化方式,每个人都被迫这样做,以便在整个代码中具有更高的一致性和可读性,我都喜欢方式,我只想知道 was/is 使用一个比另一个有什么好处吗?还是仅基于偏好的差异?
在很大程度上,它是新的语法和可读性,但仍有一些您可以使用 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....
}
所以,我们的第一个方法是这样的:
// 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
我们可以使用这种语法得到完全相同的结果,通常认为这种语法更“可读”:
// 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
现在,我们通常会有更多的约束要设置,所以我们可以用这个让它更具可读性(消除每行末尾的 .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),
])
所以...如果我们稍微复杂一点,比如说 "Keep the Bottom of the Label 20% from the bottom 会发生什么观点?
为了坚持“新的、更具可读性”的语法,我们有几个选择...
1 - 将 Label 的底部约束到视图的底部,等到布局完成 - 这样我们就知道视图的高度 - 然后将底部锚点上的 .constant
设置为 -(view height * 0.2)
。这可行,但每次标签的超级视图发生变化时(例如设备旋转时),我们都必须重新计算。
2 - 添加一个 UIView
作为“底部垫片”:
// 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),
])
这行得通,并且可以处理父视图大小的变化,但我们已经将另一个视图添加到视图层次结构中。对于这个简单的例子,没什么大不了的,但我们可能不想为复杂的布局添加一堆。
3 - 添加一个 UILayoutGuide
作为“底部间隔”:
// 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),
])
完成同样的事情,但现在我们使用 非渲染 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
}
所以,对于大多数情况,这是一个偏好问题 and/or 一致性,但你会发现偶尔的情况 是一个区别。
锚是后来添加的。它们看起来更干净,您可以更轻松地限制在安全区域,因为它有自己的锚点。
为什么有些开发人员会添加这样的约束:
NSLayoutConstraint(item: myView, attribute: .right, relatedBy: .equal, toItem: view, attribute: .right, multiplier: 1.0, constant: 20.0).isActive = true
有些像这样:
myView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 20).isActive = true
他们基本上做同样的事情......对吧?那么它们之间有什么区别呢?为什么应该使用一个而不是另一个?使用一个与另一个相比有性能差异吗?
在我工作的地方,我们的 iOS 领导专门使用 NSLayoutConstraint
初始化方式,每个人都被迫这样做,以便在整个代码中具有更高的一致性和可读性,我都喜欢方式,我只想知道 was/is 使用一个比另一个有什么好处吗?还是仅基于偏好的差异?
在很大程度上,它是新的语法和可读性,但仍有一些您可以使用 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....
}
所以,我们的第一个方法是这样的:
// 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
我们可以使用这种语法得到完全相同的结果,通常认为这种语法更“可读”:
// 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
现在,我们通常会有更多的约束要设置,所以我们可以用这个让它更具可读性(消除每行末尾的 .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),
])
所以...如果我们稍微复杂一点,比如说 "Keep the Bottom of the Label 20% from the bottom 会发生什么观点?
为了坚持“新的、更具可读性”的语法,我们有几个选择...
1 - 将 Label 的底部约束到视图的底部,等到布局完成 - 这样我们就知道视图的高度 - 然后将底部锚点上的 .constant
设置为 -(view height * 0.2)
。这可行,但每次标签的超级视图发生变化时(例如设备旋转时),我们都必须重新计算。
2 - 添加一个 UIView
作为“底部垫片”:
// 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),
])
这行得通,并且可以处理父视图大小的变化,但我们已经将另一个视图添加到视图层次结构中。对于这个简单的例子,没什么大不了的,但我们可能不想为复杂的布局添加一堆。
3 - 添加一个 UILayoutGuide
作为“底部间隔”:
// 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),
])
完成同样的事情,但现在我们使用 非渲染 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
}
所以,对于大多数情况,这是一个偏好问题 and/or 一致性,但你会发现偶尔的情况 是一个区别。
锚是后来添加的。它们看起来更干净,您可以更轻松地限制在安全区域,因为它有自己的锚点。