iOS应用的主runloop负责处理所有用户输入事件并触发响应,所有交互放在事件队列中
下图的application object会从队列中取出事件并分发到其他对象,本质上会解释来自用户的
输入事件,然后调用core obj方法,这些代码调用开发者代码,当这些方法调用返回后,控制
流回到回到主runloop上,然后开始update cycle 就是周期更新,周期更新负责重新渲染所有的视图
下图展示了应用是如何和设备交互并处理用户输入的
Update Cycle 是应用完成了所有事件的处理代码后 控制流回到主runloop的节点
正是这个时间点 开始更新布局 显示 设置约束
如果在处理事件代码中修改了一个view 那么系统会把这个view标记为需要重画的redraw
接下来的update cycle会执行这些view的更改 而且延时几乎察觉不到
一般为60fps 就是更新周期为1/60秒的时间
但是处理事件和对应的view的重绘会有时间间隔 runloop中的某个时刻的view更新可能
不是你想象的那样 所以需要知道布局视图约束的关系
下图展示出 update cycle发生在runloop尾部
布局
一个视图在屏幕上大小和位置 相对父视图的 UIView提供了用来通知系统某个view布局发生变化
的方法 还有布局重新计算后调用可重写的方法
layoutSubviews
这个方法对视图及所有子视图重新定位和大小调整 开销大 因为它会给子视图调用他们相应的layoutSubvieews
所以需要更新frame来重新定位或者更改大小时重载它
代码中不能显式调用 可以在runloop不同时间点触发layoutSubvieews
资源消耗比直接小很多
哪些方法可以触发自动刷新触发器
有许多事件会自动给视图打上 uopdate layout 标记 因此layoutSubvieews会在下个周期调用 不需要开发者手动操作
1 修改view的大小
2 新增subview
3 在UIScrollview上滚动
4 旋转设备
5 更新视图的constraints
setNeedsLayout
触发这个方法调用是最省资源的 这个方法会立刻执行并返回 返回钱不会更新视图 会在下个update cycle更新
layoutIfNeed
这个会让view触发layoutSubviews的方法 layoutIfNeed和setNeedsLayout不同的是 它会立刻调用layoutSubviews这个方法
不会等到下次的update cycle
但是没有任何操作向系统标明需要刷新视图 那么就不会调用layoutSubviews
通常配合动画执行之后的block用
视图
一个视图显示包含了颜色 文本 图片 coreGraphics绘制等视图属性 不包含本身和子视图大小和位置
和布局类似 显示也有触发更新的方法 由系统检测时更新 或者手动更新
draw方法
对视图的显示操作 类似于视图布局的layoutSubviews 但是不同于layoutSubviews draw不会触发后续子视图的方法调用
不能直接调用 而是在系统的runloop中不同节点自动调用
setNeedsDisplay
这个方法类似于布局中的layoutIfNeed 它会给内容更新视图一个标记 但在视图重绘之前就会返回
在下一个update cycle 遍历所有标记的视图 并调用他们的draw方法
约束
主要包含了视图和布局之间的关系 因为它们彼此不是孤立的 而是相互有关系的
主要分为3步 第一步是更新约束 系统计算出视图设置所有要求的约束
第二步是布局阶段 布局引擎计算视图和子视图的frame 并且将它们布局
最后一步是显示
updateConstraints
这个方法动态改变视图的约束 和布局的layoutSubvieews和显示的draw方法 这个方法只应该被重载
通常情况下 设置或者解除 或者更改约束的优先级或者常量值 或者从视图层级中移除一个视图都会
设置一个内部的标记 update constraints 这个标记会在下个周期中触发调用updateConstraints
setNeedsUpdateConstraints
这个方法保证下个更新周期更新约束
updateConstraintsIfNeeded
这个方法和布局中的layoutIfNeeded等价 如果需要会直接触发 不会等到run loop的末尾
invalidateIntrinsicContentSize
自动布局某些视图拥有intrinsicContentSize属性 这是视图根据内容获得的自然尺寸 一个视图的intrinsicContentSize
通常由里面的内容决定
调用invalidateIntrinsicContentSize 会设置一个标记表示intrinsicContentSize已经过期 需要再下一个周期重新计算
如何连接起来
下图比较好的总结
下面的流程总结了update cycle和event loop之间的交互
如果显式调用layoutIfNeeded 或者 updateConstraintsIfNeeded 开销会很大
可以通过设置“update constraints”,“update layout” 或者 “needs display” 标记
在下个刷新周期进行布局 视图 约束的更新