UIViewController 生命周期
问题
UIViewController 的生命周期方法有哪些?页面跳转时各 VC 的回调顺序是什么?
答案
完整生命周期
各方法用途
| 方法 | 调用时机 | 常见用途 |
|---|---|---|
loadView | 创建 view 层级 | 自定义 root view(不调 super) |
viewDidLoad | view 加载到内存后(仅一次) | 初始化 UI、网络请求 |
viewWillAppear | view 即将显示 | 刷新数据、注册通知 |
viewDidAppear | view 已显示 | 启动动画、开始定位 |
viewWillDisappear | view 即将消失 | 保存数据、注销通知 |
viewDidDisappear | view 已消失 | 释放资源 |
viewWillLayoutSubviews | 即将布局子视图 | 调整 frame |
viewDidLayoutSubviews | 布局完成 | 获取最终 frame |
页面跳转的回调顺序
Push B:
A: viewWillDisappear
B: viewWillAppear
A: viewDidDisappear
B: viewDidAppear
Pop B(返回 A):
B: viewWillDisappear
A: viewWillAppear
B: viewDidDisappear
A: viewDidAppear
B: deinit
Present B(模态):
// iOS 13+ 默认 .automatic(pageSheet,A 不触发 disappear)
B: viewWillAppear
B: viewDidAppear
// .fullScreen
A: viewWillDisappear
B: viewWillAppear
A: viewDidDisappear
B: viewDidAppear
iOS 13 模态变化
iOS 13+ 默认模态样式为 .pageSheet(非全屏),此时 A 的 viewWillDisappear / viewDidDisappear 不会被调用。如果需要全屏模态,需设置 modalPresentationStyle = .fullScreen。
View 的布局触发流程
setNeedsLayout:标记需要重新布局(异步,不立即执行)layoutIfNeeded:如果有标记,立即执行布局layoutSubviews:实际执行布局,子类重写(必须调 super)
常见面试问题
Q1: viewDidLoad 和 viewWillAppear 的区别?
答案:
viewDidLoad:view 第一次加载到内存时调用,只调一次。适合做初始化配置viewWillAppear:每次 view 即将显示时都会调用(push 回来也会调)。适合做数据刷新
Q2: 如何保证在获取到正确的 frame 后再做操作?
答案:在 viewDidLayoutSubviews 中操作,此时 AutoLayout 已计算完毕。注意该方法可能被多次调用。
Q3: loadView 什么时候需要重写?
答案:当你需要完全自定义 root view 时(不使用 Storyboard/Xib 且不需要默认的 UIView):
override func loadView() {
// 不要调 super.loadView()
view = MyCustomView()
}
如果只是在默认 view 上添加子视图,在 viewDidLoad 中操作即可。