UIViewController 的生命周期方法反映了控制器从创建到销毁的完整过程,这些方法按特定顺序调用,用于处理视图加载、布局、显示和销毁等逻辑。以下是主要生命周期方法的执行顺序及作用:
一、初始化阶段
-
init系列方法- 控制器的初始化方法(如
init(nibName:bundle:)或自定义初始化方法),最早被调用。 - 用于初始化数据、设置属性等(此时视图尚未创建)。
- 控制器的初始化方法(如
-
loadView()- 负责创建控制器的根视图(
view属性)。 - 若未使用 XIB/Storyboard,需重写此方法手动创建
view(如self.view = UIView());若使用 XIB,则系统会自动加载。 - 注意:不要直接调用此方法,由系统自动触发。
- 负责创建控制器的根视图(
二、视图加载阶段
-
viewDidLoad()- 视图(
view)加载完成后调用(从 XIB 加载或loadView创建后)。 - 主要用于初始化视图控件(如按钮、标签)、设置约束、绑定数据等。
- 此时视图的
bounds可能尚未确定(不适合布局计算)。
- 视图(
三、布局与显示阶段
-
viewWillAppear(_ animated: Bool)- 视图即将显示在屏幕上时调用。
- 可在此更新数据、调整状态栏样式、启动动画等。
-
viewWillLayoutSubviews()- 即将计算子视图布局时调用(每次视图尺寸变化都会触发,如旋转屏幕、弹窗)。
- 适合调整子视图布局相关的属性(如
frame、transform)。
-
viewDidLayoutSubviews()- 子视图布局计算完成后调用。
- 此时可获取到准确的视图尺寸(
bounds),适合进行依赖尺寸的布局调整(如手动计算位置)。
-
viewDidAppear(_ animated: Bool)- 视图完全显示在屏幕上后调用。
- 可在此启动需要可见性的任务(如开始播放视频、请求网络数据)。
四、视图消失阶段
-
viewWillDisappear(_ animated: Bool)- 视图即将从屏幕消失时调用。
- 可在此暂停动画、保存数据、隐藏键盘等。
-
viewDidDisappear(_ animated: Bool)- 视图完全消失后调用。
- 适合释放不再需要的资源(如取消网络请求、移除通知监听)。
五、内存警告与销毁阶段
-
didReceiveMemoryWarning()- 系统内存不足时调用,用于释放非必要资源。
- 可重写此方法,清理缓存、销毁不可见的视图等(如
self.view = nil,触发loadView重新加载)。
-
deinit- 控制器销毁时调用,是生命周期的最后一步。
- 用于释放所有资源(如移除定时器、解除代理引用,避免内存泄漏)。
完整执行顺序(简化)
plaintext
初始化(init) → loadView() → viewDidLoad()
→ viewWillAppear() → viewWillLayoutSubviews() → viewDidLayoutSubviews()
→ viewDidAppear()
→ (视图可见,可能多次触发 layout 方法)
→ viewWillDisappear() → viewDidDisappear()
→ (内存警告时:didReceiveMemoryWarning())
→ deinit(控制器销毁)
关键注意事项
-
布局时机:涉及尺寸的计算应放在
viewDidLayoutSubviews()中,而非viewDidLoad()(此时尺寸可能不正确)。 -
复用与销毁:
UINavigationController或UITabBarController中的控制器,在切换时只会执行viewWillAppear/viewDidDisappear等方法,不会立即销毁(deinit可能延迟调用)。 -
避免重复初始化:
viewDidLoad仅在视图首次加载时调用,重复显示时不会触发,需区分初始化逻辑和刷新逻辑。
理解这些方法的调用时机,有助于合理安排数据加载、UI 初始化和资源释放,写出更规范的控制器代码。
注意横竖屏切换
当设备横竖屏切换时,viewWillLayoutSubviews 和 viewDidLayoutSubviews 会被再次调用。
原因如下:
-
横竖屏切换会导致视图(
UIView)的尺寸(bounds)发生变化(宽高互换)。 -
系统为了重新计算和调整子视图的布局,会触发布局更新流程,其中就包括这两个方法:
- 首先调用
viewWillLayoutSubviews(即将开始重新布局子视图) - 系统内部计算并更新子视图的位置和尺寸
- 然后调用
viewDidLayoutSubviews(子视图布局已完成)
- 首先调用
实际应用场景
这两个方法是处理横竖屏布局适配的重要时机,例如:
swift
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// 横竖屏切换后,在此获取最新的视图尺寸并调整布局
if view.bounds.width > view.bounds.height {
// 横屏布局逻辑
horizontalLayout()
} else {
// 竖屏布局逻辑
verticalLayout()
}
}
注意
-
除了横竖屏切换,其他导致视图尺寸变化的情况(如弹出键盘、父视图尺寸改变)也会触发这两个方法。
-
重写时必须调用
super方法(super.viewWillLayoutSubviews()/super.viewDidLayoutSubviews()),否则可能导致布局异常。
因此,这两个方法是处理动态布局变化的核心回调,尤其适合横竖屏适配场景。