UIView生命周期

50 阅读3分钟

UIView 作为 iOS 界面的基础组件,虽然没有像 UIViewController 那样明确的 “生命周期方法”,但它的创建、显示、布局、销毁过程会触发一系列关键方法和事件,可视为其生命周期的重要节点。以下是 UIView 从创建到销毁的核心流程及相关方法:

一、创建阶段

  1. 初始化

    • 通过初始化方法创建实例,如 init(frame:) 或 init(coder:)(从 XIB/Storyboard 加载时调用)。
    • 此时视图的基本属性(如 framebackgroundColor)可被设置,但尚未加入视图层级。
  2. 加入视图层级

    • 调用 addSubview(_:) 将视图添加到父视图后,视图进入视图树(view hierarchy)。
    • 此时会触发 didMoveToSuperview() 方法,可在此处理与父视图相关的逻辑。

二、布局与显示阶段

  1. 布局计算

    • layoutSubviews() :当视图的尺寸(bounds)变化、子视图增减或调用 setNeedsLayout() 时触发,是布局子视图的核心方法。
      需重写此方法来手动计算子视图的 frame 或 center(自动布局会在此阶段应用约束)。

      swift

      override func layoutSubviews() {
          super.layoutSubviews() // 必须调用父类方法
          // 调整子视图布局
          subview.frame = CGRect(x: 10, y: 10, width: bounds.width - 20, height: 50)
      }
      
  2. 绘制内容

    • draw(_ rect: CGRect) :当视图首次显示或调用 setNeedsDisplay() 时触发,用于自定义绘制内容(如绘制图形、文本)。
      注意:此方法开销较大,避免频繁调用。
  3. 显示到屏幕

    • 经过布局和绘制后,视图最终渲染到屏幕上。此时可通过 isHidden 属性控制可见性。

三、状态变化阶段

  1. 窗口关联变化

    • didMoveToWindow() :当视图加入窗口(UIWindow)或从窗口移除时调用。可在此处理与窗口相关的逻辑(如键盘事件、全局手势)。
  2. 尺寸 / 位置变化

    • 当 framebounds 或 center 改变时,会间接触发 layoutSubviews()(若尺寸变化)。
    • 可通过监听 bounds 变化(KVO)来捕获尺寸调整事件。

四、销毁阶段

  1. 从视图树移除

    • 调用 removeFromSuperview() 后,视图从父视图中移除,触发 willMove(toSuperview: nil) 和 didMoveToSuperview()(此时 superview 为 nil)。
  2. 释放资源

    • 当视图不再被强引用(如父视图销毁、无其他指针指向),会调用 deinit(Swift)或 dealloc(Objective-C),完成最终的资源释放(如移除定时器、解除监听)。

核心流程总结

plaintext

初始化(init 加入父视图(didMoveToSuperview)  加入窗口(didMoveToWindow)  
 布局子视图(layoutSubviews)  绘制内容(draw)  显示在屏幕  
 (尺寸变化时重复 layoutSubviews)  
 从父视图移除(removeFromSuperview)  销毁(deinit

关键说明

  • UIView 的生命周期依赖于视图层级和引用关系,脱离视图树的视图若仍被强引用,不会被销毁。

  • 自动布局(Auto Layout)会在 layoutSubviews() 阶段生效,因此手动布局逻辑需在此方法中实现。

  • 避免在 draw(_:) 中执行复杂计算,优先使用图层(CALayer)属性(如 cornerRadiusborderWidth)实现样式,性能更优。

理解 UIView 的生命周期,有助于合理安排布局、绘制和资源管理,避免布局异常或内存泄漏。