iOS Layout Engine 与 Render Loop

iOS引入自动布局之后,除了直接设置frame/bounds外,所有能影响layout的属性改动(子视图、布局约束、优先级、对齐方式等)都会经过layout engine计算转换为对应的frame值

iOS layout engine

layout engine计算出每个视图(Rect)对应的尺寸和坐标值,再由GPU最终渲染呈现到屏幕上

iOS layout engine render

整个约束改变到渲染展示的完整过程为:

  1. Runloop收集修改过的布局约束发送到layout engine
  2. layout engine根据最新的约束更新所有受影响的约束并重新布局绘制
  3. Runloop拿到layout engine的绘制点数据等,通过GPU等将对应的视图渲染并展示出来

iOS layout cycle

Render Loop

在这个过程中layout engine的工作部分被称为Render loop,可以总结为三个步骤:

  1. 更新/修改约束:从子视图向上层逐级更新约束,一直到window
  2. 调整layout:从父视图向下逐层调整layout
  3. 渲染及绘制:从父视图向下逐层渲染绘制

iOS layout render loop

这三个步骤具体实现则是View中的一系列方法,为了保证layout的准确,每次调整都会完整的调用这些API,同时API内部也去除了很多重复性的工作:
Update

1
2
3
- (void)updateConstraints;
- (void)setNeedsUpdateConstraints;
- (void)updateConstraintsIfNeeded;

Layout

1
2
3
- (void)layoutSubviews;
- (void)setNeedsLayout;
- (void)layoutIfNeeded;

Display

1
2
- (void)drawRect:(CGRect)rect;
- (void)setNeedsDisplay;

Layout engine

当添加并激活一个约束(Constraint)的时候,layout engine会将它表示的关系item1.attribute1 = multiplier × item2.attribute2 + constant转换为一个多元一次方程

iOS layout engine function

每个View上的多个约束的多个方程组成了一个方程组。layout engine会将这个方程组解出来,方程组的解就作为对应View的frame。

iOS layout engine equation

如果没有解,则表示约束缺失;有多个解则是约束冲突。优先级的作用就是有多个解时选择使用优先级较高的方程组的解

在计算出方程组的解之后,layout engine会通知对应View调用父视图setNeedsLayout()方法来更新约束;从这可以知道更新约束的过程是自底向上的

iOS layout engine update

在更新约束完成之后,进入layout调整阶段;每个View会从engine中复制其子视图的所有坐标数据,然后调用layoutSubviews()方法重新为子视图调整layout;从这点可以知道Layout调整阶段是自顶向下的

iOS layout engine layout

剩下的绘制过程则是检测是否有自定义的绘制图层、路径等;如果有就将绘制的信息打包发送到GPU渲染展示

优化layout性能

每次约束规则删除、添加,都会导致刷新视图并重新经历一遍引擎计算、自底向上的setNeedsLayout()的约束更新和自顶向下的Layout调整;所以我们要尽量避免频繁的增减约束

  • 应该引用需要修改的约束,并且只在必要的时候修改它
  • 尽量不要删除所有的约束(有时一个View上的所有约束不仅仅是它自身的约束,还会有子视图及兄弟视图的约束)
  • 尽量用hide的方式来替代删除视图的操作

systemLayoutSizeFitting()

systemLayoutSizeFitting()虽然能根据Layout来自动计算其约束,但是这个方法实质是通过创建一个引擎获取对应的size,然后再将引擎销毁;所以会增加一次额外的重复,大大降低了性能

intrinsicContentSize

intrinsicContentSize属性返回一个视图固有的尺寸,设置之后layout engine不会再计算它的frame;可以提高性能。

当固有属性改变之后,调用- (void)invalidateIntrinsicContentSize;方法可以让layou engine根据最新的尺寸计算位置;也可以调用- (void)layoutSubviews;

优先级

内容吸附(content hugging)优先级默认 250: 优先级低的会被拉伸;优先级高的被吸附
内容压缩阻力(compression resistance)优先级默认 750: 优先级低的会被压缩,优先级高的抗压缩阻力比较大

DEBUG

Xcode 启动参数 -UIViewLayoutFeedbackLoopDebuggingThreshold & -NSViewLayoutFeedbackLoopDebuggingThreshold 50~1000

systemLayoutSizeFitting(_ targetSize) 会创建一个引擎计算尺寸然后再将引擎销毁,消耗性能

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2017-2021 HonQi

请我喝杯咖啡吧~