iOS 布局:layoutSubviews与它的家人们

2,316 阅读2分钟

一个倾向于强化巩固记忆的知识点复习。


在一个 Runloop 处理完事件后,最后进入 Update cycle:

  1. 更新约束
  2. 布局:引擎计算视图和子视图的 frame 并布局
  3. 显示:重绘视图

Step1:更新约束

updateConstraints()

类似于 layoutSubviews 的机制。

只应该在此方法中实现必须要更新的约束,静态的约束应在interface builder、视图的初始化方法或 viewDidLoad() 方法中指定。

不应直接调用,应调用触发方法让系统处理

自动标记约束发生变化,在下一个周期会触发 updateConstraints 的行为:

  • 设置/解除约束
  • 更改约束优先级
  • 从视图层级 (view hierarchy) 中移除视图

setNeedUpdateConstraints()

类似于 setNeedsLayout 的机制,手动标记发生变化,下周期调用 updateConstraints 更新

updateConstraintsIfNeeded()

类似于 layoutIfNeeded 的机制,检查视图的约束是否被标记发生变化,如有变化立即调用 updateConstraints

invalidateIntrinsicContentSize()

设置一个标记表示这个视图的 intrinsicContentSize 已经过期,需要在下一个布局阶段重新计算。

视图的 intrinsicContentSize:视图根据自己内容得到的自然尺寸,通常由包含元素的约束约定,也可以自定义

Step2:布局视图

layoutSubviews()

处理对视图及其所有子视图的重新定位和大小调整 会调用子视图的 layoutSubviews,开销很大

不应直接调用,应调用触发方法让系统处理

自动标记视图的布局发生变化,在下一个周期会触发 layoutSubviews 的行为:

  • 通过 setFrame 改变视图的 bounds(不仅是位置)
  • add subview
  • UIScrollView 的滚动(layoutSubviews 会在 UIScrollView 和它的父 view 上被调用)
  • 旋转设备:调用当前 controller 的 self.view 的 layoutSubviews
  • 更新视图的 constraints

setNeedsLayout()

手动标记该视图的布局发生变化,在下一个周期调用 layoutSubviews 更新

layoutIfNeeded()

检查视图的布局是否被标记发生变化,如有变化立即调用 layoutSubviews

Step3:绘制

drawRect()

类似于 layoutSubviews 的机制,但不会触发对子视图方法的调用

不应直接调用,应调用触发方法让系统处理

自动标记视图显示发生变化,在下一个周期会触发 drawRect 的行为:

  • 改变视图的 bounds

setNeedsDisplayInRect()

类似于 setNeedsLayout 的机制,手动标记发生变化,下周期调用 drawRect 更新

显示方法没有类似 layoutIfNeeded 的方法