1. AutoLayout的核心
苹果公司推出的AutoLayout,是一个基于约束,动态计算视图大小和位置的库,以布局引擎系统Layout Engine
为核心,采用了 Cassowary布局算法,在简化布局思路的同时,还保证了布局的高效性。
布局算法Cassowary能够有效解析线性等式系统和线性不等式系统,用来表示用户界面中那些相等关系和不等关系,通过设定约束来表示一个视图相对于另一个视图的位置。
Cassowary简化了布局思路,在运行时动态的计算视图位置。布局思路简化了,也使界面相关代码更容易维护。
布局引擎系统Layout Engine则统一管理了布局的创建、更新、销毁,将视图的约束、优先级、固定大小通过计算转换成最终的大小和位置。
在Layout Engine中,每当约束发生变化,会重新计算布局,获取到布局后调用superview.setNeedLayout(),然后触发Deffered Layout Pass做容错处理,然后Layout Engine会从上到下调用layoutSubviews()来确定各子视图的位置(通过Cassowary算法计算),算出来后将子视图的frame从Layout Engine里拷贝出来,然后进行绘制、渲染,得到我们眼中看到的UI效果。
-
总结:AutoLayout利用约束来控制视图的大小和位置,系统会在运行时通过设置的约束计算得到frame,然后绘制出来显示。
2. AutoLayout的性能
在iOS12之前,AutoLayout在处理多层级嵌套时,开销呈指数级跃增,但是从iOS12开始,苹果补齐了这一漏洞,所以在iOS12及以后,我们就开始放心的使用AutoLayout,而不用担心性能问题。
更多关于AutoLayout的性能问题及解决方法,请查看WWDC2018-202
3. AutoLayout常见的问题
(1)几个更新方法的区别
setNeedsLayout:告知页面需要更新,但是不会立刻开始更新。
layoutIfNeeded:如果有需要刷新的标记,立即调用layoutSubviews进行布局;如果没有标记,不会调用layoutSubviews。如果希望立刻生成新的frame需要调用此方法,利用这点一般布局动画可以在更新布局后直接使用这个方法让动画生效。
layoutSubviews:对subviews进行布局,不能主动调用,需要的时候在子类重写,系统会在合适的时候自动调用。
注意 : 如果要立即刷新frame,要先调用setNeedsLayout(),把标记设为需要布局,然后马上调用layoutIfNeeded(),实现布局。
setNeedsUpdateConstraints:告知需要更新约束,但是不会立刻开始
updateConstraintsIfNeeded:告知立刻更新约束
updateConstraints:系统更新约束
(2)系统调用layoutSubviews的时机
init初始化不会触发layoutSubviews,但是使用initWithFrame进行初始化且rect不为zero时,会调用layoutSubviews。
addSubview的时候会触发系统调用layoutSubviews。
当view的frame发生改变的时候触发layoutSubviews。
滚动一个UIScrollView会触发layoutSubviews。
旋转Screen会触发父UIView上的layoutSubviews事件。
改变一个UIView大小的时候也会调用父UIView上的layoutSubviews事件。
(3)Intrinsic content size 固有内容大小
Intrinsic content size 就是固有内容大小,对应的系统方法是
intrinsicContentSize
,重写UIView的固有内容大小后,我们就可以更灵活的使用UIView了。在xib中想使用Intrinsic Size时,记得先在子类中重写
intrinsicContentSize
,然后在Xib选中Placeholder
,才不会报错哦。
class TestView: UIView {
override var intrinsicContentSize: CGSize {
return CGSize(width: 300, height: 800)
}
}
(4)手写autoLayout,写在哪里最好
如果是自定义的View,写在
init()
方法中如果是在ViewController中,写在
viewDidLoad()
中
(5)SizeClass适配
关于SizeClass,请看我的另一篇简书适配iPad和iPhone及其横竖屏
(6)UIStackView
对一些特定布局时,使用UIStackView很节省时间,iOS9.0之后可用
- Axis : 子控件的布局方向,水平或垂直
- Alignment :子控件对齐方式
- Distribution : 子控件分布方式
- Spacing : 控件和控件之间的最小间距
(7)UITableView的高度计算
- 手工计算,并缓存高度
从xib中加载一个Cell,然后给这个Cell中的控件赋值,然后利用systemLayoutSizeFitting
计算高度,然后缓存高度到Model中
let tableViewCell = Bundle.main.loadNibNamed("tableViewCell", owner: self, options: nil)?.last
//先给给tablViewCell中的控件赋值,然后在计算高度
if let tableViewCell = tableViewCell as? UITableViewCell{
let cellSize = tableViewCell.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
}
//然后在缓存高度到Model中
- 使用Self-Sizing自动计算高度,当布局满足
self-satisfied
时,系统会自动计算高度,缺点是慢,因为没有缓存高度,每次都要计算一次
tableView.estimatedRowHeight = 300
tableView.rowHeight = UITableView.automaticDimension