对于常常写 UI 页面的 iOS 开发者来讲,Auto Layout 是提升开发效率的一大利器。但若是使用不当的话,也会对项目性能形成损害。因此,知道如何正确的使用 Auto Layout 仍是很重要的。markdown
本文首先会经过一个例子来讲明下什么是约束流失,了解约束流失后,会带你们看一下 Render Loop 的工做流程。而后会说下 Auto Layout 的背后实现原理。最后,了解下 Auto Layout 特定状况下的最佳作法。让咱们开始吧。ide
什么是约束流失?oop
答:对于同一视图,进行没必要要的删除和从新添加约束。布局
经过下面的例子来解释一下:性能
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()
}
复制代码
上面的代码作了三件事:spa
移除,从新建立。直观上看这不是高效的代码,事实上也确实不是。它会给性能带来压力,由于这段代码会每秒执行不少次。code
咱们能够经过一个简单的 if 判断来避免无用的执行。orm
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 涉及更新约束、布局、显示三个阶段。下面是这三个阶段的流向: rem
更新布局是从底层视图一步步流向 window ,而布局恰巧相反,从 window 流向底层视图,显示则和布局流向一致。
Render Loop 的优势:能够避免无用的工做;注意事项:会运行不少次。因此咱们应该谨慎使用。
当咱们给控件添加约束时,下面的四个值必须能计算出来,不然视图会显示不正常。
控件需计算的四个值:minX、minY、width、height。
好比上面的约束会替换成下面的公式:
也就是说 Auto Layout 就是用二元一次方程式来求出各个参数的值。
当咱们写下上面的约束时,系统会建立一个 Engine,Engine 去负责约束的计算,最终 Engine 会把 minX、minY、width、height 的具体值返回给 View,View 则根据返回值调用 setNeedsLayout()
来更新视图。