Day2-View 布局流程
View 布局流程详解
一、布局流程总览
iOS 的视图布局流程由 UIKit 管理,核心遵循「从父到子、从外向内」的递归更新方式。涉及的关键方法有:
layoutSubviews
:真正进行子视图位置布局的地方setNeedsLayout
:标记视图为需要重新布局layoutIfNeeded
:立即强制布局
布局流程如下:
属性变化(如 frame)或手动触发
↓
调用 setNeedsLayout(标记需要布局)
↓
下一个 runloop 执行 layoutIfNeeded
↓
系统递归调用 layoutSubviews 进行布局
二、关键方法详解
1. setNeedsLayout
- 作用:标记当前视图为“需要重新布局”
- 调用时机:当视图状态或依赖数据变化时
- 注意:不会立即执行
layoutSubviews
,而是在下一个 runloop 进行布局
1 | [self.view setNeedsLayout]; |
2. layoutIfNeeded
- 作用:立即执行布局流程(如果已标记)
- 常用场景:动画中强制更新 layout,确保视图状态正确
1
[self.view layoutIfNeeded];
3. layoutSubviews
- 作用:布局子视图,子类通过重写此方法来自定义布局逻辑
- 调用方式:系统自动调用,或通过 layoutIfNeeded 间接触发
1
2
3
4- (void)layoutSubviews {
[super layoutSubviews];
self.label.frame = CGRectMake(10, 10, 100, 40);
}
三、布局触发时机
布局流程会在以下情况下自动触发:
- 视图初始化完成后首次添加到视图层级中
- frame / bounds / center 属性改变
- 调用 setNeedsLayout + layoutIfNeeded
- 屏幕旋转、设备方向变化
- Auto Layout 约束变化
四、Auto Layout 参与布局流程
当使用 Auto Layout(自动布局)时:
- 不推荐在 layoutSubviews 中设置 frame
- 更新约束应在 updateConstraints 方法中完成
- updateConstraints → layoutSubviews → drawRect 是完整的调用链
1
2
3
4- (void)updateConstraints {
// 更新约束
[super updateConstraints];
}
五、UIView 与 CALayer 的关系
- 每个 UIView 都有一个对应的 CALayer
- UIView.frame 实际上是操作 CALayer.frame
- 布局本质上是在更新图层的属性
1
NSLog(@"%@", NSStringFromCGRect(self.view.layer.frame));
六、文字版布局流程图
1 | [视图属性变更] |
七、面试高频问题整理
Q1:layoutSubviews 和 drawRect 有什么区别?
- layoutSubviews:用于更新子视图的布局
- drawRect:用于自定义视图绘图(如 Core Graphics)
Q2:如何立即触发布局?
1 | [self.view setNeedsLayout]; |
Q3:什么时候会调用 layoutSubviews?
- 添加到父视图后
- frame 改变
- setNeedsLayout + layoutIfNeeded
- Auto Layout 触发
Q4:如何在动画中确保布局生效?
1 | [UIView animateWithDuration:0.3 animations:^{ |