对于经常写 UI 页面的 iOS 开发者来说,Auto Layout 是提高开发效率的一大利器。但如果使用不当的话,也会对项目性能造成损害。所以,知道如何正确的使用 Auto Layout 还是很重要的。
本文首先会通过一个例子来说明下什么是约束流失,了解约束流失后,会带大家看一下 Render Loop 的工作流程。然后会说下 Auto Layout 的背后实现原理。最后,了解下 Auto Layout 特定情况下的最佳做法。让我们开始吧。
约束流失
什么是约束流失?
答:对于同一视图,进行不必要的删除和重新添加约束。
通过下面的例子来解释一下:
var myConstraints = [NSLayoutConstraint]()
let text1 = UILabel()
let text2 = UILabel()
override func updateViewConstraints() {
// step1
NSLayoutConstraint.deactivate(myConstraints)
myConstraints.removeAll()
// step2
let views = ["text1": text1, "text2": text2]
myConstraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|-[text1]-[text2]",
options: [.alignAllFirstBaseline],
metrics: nil,
views: views)
myConstraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|-[text1]-|",
options: [.alignAllFirstBaseline],
metrics: nil,
views: views)
// step3
NSLayoutConstraint.activate(myConstraints)
super.updateViewConstraints()
}
上面的代码做了三件事:
- step1:将之前的约束失效并移除。
- step2:重新设置约束。
- step3:生效重新设置的约束。
移除,重新创建。直观上看这不是高效的代码,事实上也确实不是。它会给性能带来压力,因为这段代码会每秒执行很多次。
我们可以通过一个简单的 if 判断来避免无用的执行。
override func updateViewConstraints() {
if myConstraints.isEmpty {
var constrains = [NSLayoutConstraint]()
let views = ["text1": text1, "text2": text2]
constrains += NSLayoutConstraint.constraints(withVisualFormat: "H:|-[text1]-[text2]",
options: [.alignAllFirstBaseline],
metrics: nil,
views: views)
constrains += NSLayoutConstraint.constraints(withVisualFormat: "V:|-[text1]-|",
options: [.alignAllFirstBaseline],
metrics: nil,
views: views)
NSLayoutConstraint.activate(constrains)
myConstraints = constrains
}
super.updateViewConstraints()
}
在更新约束的时候,首先判断当前约束是否有值,若有值则直接跳过;无值再创建约束赋值。这样就避免了多次执行移除,重新创建
的流程。
Render Loop 概览
Render Loop 涉及更新约束、布局、显示三个阶段。下面是这三个阶段的流向:
更新布局是从底层视图一步步流向 window ,而布局恰巧相反,从 window 流向底层视图,显示则和布局流向一致。
Render Loop 的优点:可以避免无用的工作;注意事项:会运行很多次。所以我们应该谨慎使用。
Auto Layout 的背后实现
当我们给控件添加约束时,下面的四个值必须能计算出来,否则视图会显示不正常。
控件需计算的四个值:minX、minY、width、height。
比如上面的约束会替换成下面的公式:
- text1
- text1.minX = 20
- text1.minY = 30
- text1.width = 100
- text1.height = 20
- text2
- text2.minX = text1.minX + text1.width + 10 最终得出 130
- text2.minY = 30
- text2.width = 100
- text2.height = 20
也就是说 Auto Layout 就是用二元一次方程式来求出各个参数的值。
当我们写下上面的约束时,系统会创建一个 Engine,Engine 去负责约束的计算,最终 Engine 会把 minX、minY、width、height 的具体值返回给 View,View 则根据返回值调用 setNeedsLayout()
来更新视图。
最佳做法
- 特定情况下需要隐藏控件
- 直接使用 hidden 属性就好,不要移除控件或者约束。
- 刷新视图
- 避免移除所有约束,最好在当前约束的基础上修改。
- 对于不变的约束确保只添加一次。
- 只修改需要改变的约束。
总结
- 不要让约束流失。
- 约束底层计算只是简单的方程式计算。
- 只为你的功能耗费性能,不做无谓的消耗。
- 避免添加有歧义的约束,比如要求 view 的 width 即是 50 又是 200。