UIView 作为 iOS 界面的基础组件,虽然没有像 UIViewController 那样明确的 “生命周期方法”,但它的创建、显示、布局、销毁过程会触发一系列关键方法和事件,可视为其生命周期的重要节点。以下是 UIView 从创建到销毁的核心流程及相关方法:
一、创建阶段
-
初始化
- 通过初始化方法创建实例,如
init(frame:)或init(coder:)(从 XIB/Storyboard 加载时调用)。 - 此时视图的基本属性(如
frame、backgroundColor)可被设置,但尚未加入视图层级。
- 通过初始化方法创建实例,如
-
加入视图层级
- 调用
addSubview(_:)将视图添加到父视图后,视图进入视图树(view hierarchy)。 - 此时会触发
didMoveToSuperview()方法,可在此处理与父视图相关的逻辑。
- 调用
二、布局与显示阶段
-
布局计算
-
layoutSubviews():当视图的尺寸(bounds)变化、子视图增减或调用setNeedsLayout()时触发,是布局子视图的核心方法。
需重写此方法来手动计算子视图的frame或center(自动布局会在此阶段应用约束)。swift
override func layoutSubviews() { super.layoutSubviews() // 必须调用父类方法 // 调整子视图布局 subview.frame = CGRect(x: 10, y: 10, width: bounds.width - 20, height: 50) }
-
-
绘制内容
draw(_ rect: CGRect):当视图首次显示或调用setNeedsDisplay()时触发,用于自定义绘制内容(如绘制图形、文本)。
注意:此方法开销较大,避免频繁调用。
-
显示到屏幕
- 经过布局和绘制后,视图最终渲染到屏幕上。此时可通过
isHidden属性控制可见性。
- 经过布局和绘制后,视图最终渲染到屏幕上。此时可通过
三、状态变化阶段
-
窗口关联变化
didMoveToWindow():当视图加入窗口(UIWindow)或从窗口移除时调用。可在此处理与窗口相关的逻辑(如键盘事件、全局手势)。
-
尺寸 / 位置变化
- 当
frame、bounds或center改变时,会间接触发layoutSubviews()(若尺寸变化)。 - 可通过监听
bounds变化(KVO)来捕获尺寸调整事件。
- 当
四、销毁阶段
-
从视图树移除
- 调用
removeFromSuperview()后,视图从父视图中移除,触发willMove(toSuperview: nil)和didMoveToSuperview()(此时superview为nil)。
- 调用
-
释放资源
- 当视图不再被强引用(如父视图销毁、无其他指针指向),会调用
deinit(Swift)或dealloc(Objective-C),完成最终的资源释放(如移除定时器、解除监听)。
- 当视图不再被强引用(如父视图销毁、无其他指针指向),会调用
核心流程总结
plaintext
初始化(init) → 加入父视图(didMoveToSuperview) → 加入窗口(didMoveToWindow)
→ 布局子视图(layoutSubviews) → 绘制内容(draw) → 显示在屏幕
→ (尺寸变化时重复 layoutSubviews)
→ 从父视图移除(removeFromSuperview) → 销毁(deinit)
关键说明
-
UIView的生命周期依赖于视图层级和引用关系,脱离视图树的视图若仍被强引用,不会被销毁。 -
自动布局(Auto Layout)会在
layoutSubviews()阶段生效,因此手动布局逻辑需在此方法中实现。 -
避免在
draw(_:)中执行复杂计算,优先使用图层(CALayer)属性(如cornerRadius、borderWidth)实现样式,性能更优。
理解 UIView 的生命周期,有助于合理安排布局、绘制和资源管理,避免布局异常或内存泄漏。