Day2-View 布局流程

YVTU

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
2
3
4
5
6
7
8
9
[视图属性变更]

[setNeedsLayout] → 标记布局

[下一个 runloop]

[layoutIfNeeded] → 判断并触发布局

[layoutSubviews] → 更新子视图 frame

七、面试高频问题整理

Q1:layoutSubviews 和 drawRect 有什么区别?

  • layoutSubviews:用于更新子视图的布局
  • drawRect:用于自定义视图绘图(如 Core Graphics)

Q2:如何立即触发布局?

1
2
[self.view setNeedsLayout];
[self.view layoutIfNeeded];

Q3:什么时候会调用 layoutSubviews?

  • 添加到父视图后
  • frame 改变
  • setNeedsLayout + layoutIfNeeded
  • Auto Layout 触发

Q4:如何在动画中确保布局生效?

1
2
3
4
[UIView animateWithDuration:0.3 animations:^{
// 修改布局前置条件
[self.view layoutIfNeeded]; // 强制立即布局
}];

八、参考资料