基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

SwiftUI 视图的生命周期方法有哪些?

知识点图片

SwiftUI 的视图(View)本质上是结构体(Struct),是值类型。这意味着它们会被频繁地创建和销毁。因此,SwiftUI 的生命周期管理与 UIKit(基于类和引用类型)有很大不同。

SwiftUI 不再依赖 viewDidLoad 这种控制器级别的方法,而是通过修饰符(Modifiers)来响应视图在层级结构中的变化。

以下是 SwiftUI 中核心的生命周期方法:

1. 视图出现与消失 (Appear / Disappear)

这是最常用的两个生命周期修饰符,对应 UIKit 的 viewWillAppear/viewDidAppearviewWillDisappear/viewDidDisappear

  • .onAppear(perform:)

    • 触发时机:当视图被插入到视图层级结构中并显示在屏幕上时。
    • 用途:开始动画、请求数据(如果不需要 async/await)、埋点统计。
    • 注意:在 ListLazyVStack 中,滚动导致视图复用时会重复触发。
  • .onDisappear(perform:)

    • 触发时机:当视图从视图层级结构中移除时。
    • 用途:停止动画、取消不再需要的操作、保存临时数据。
plaintext
Text("Hello World")
    .onAppear {
        print("视图出现了")
    }
    .onDisappear {
        print("视图消失了")
    }

2. 异步任务 (Task) - iOS 15+

这是现代 SwiftUI 开发中替代 onAppear 进行异步操作的首选方式。

  • .task(priority:action:)
    • 触发时机:与 onAppear 类似,当视图出现时触发。
    • 特点
      1. 支持 Swift Concurrency (async/await)。
      2. 自动取消:当视图消失(触发 onDisappear)时,该 Task 会自动被取消。
    • 用途:网络请求、加载重型数据。
plaintext
Text("Loading Data...")
    .task {
        // 这里可以写 await 代码
        await loadData() 
    } // 视图消失时,loadData 会自动收到取消信号
  • .task(id:priority:action:)
    • 变体:当指定的 id 值发生变化时,会取消旧任务并重新运行新任务。类似于 onChange + task 的结合。

3. 状态变化 (Change)

SwiftUI 是数据驱动的,因此监听数据变化也是生命周期的一部分。

  • .onChange(of:perform:)
    • 触发时机:当指定的 StateBindingEnvironment 值发生变化时。
    • 用途:根据数据变化执行逻辑(如表单验证、联动更新)。
plaintext
@State private var count = 0

Text("Count: \(count)")
    .onChange(of: count) { oldValue, newValue in // iOS 17+ 写法
        print("Count 从 \(oldValue) 变成了 \(newValue)")
    }

4. 应用层级生命周期 (ScenePhase)

如果你需要监听整个 App 的状态(如进入后台、回到前台),需要在视图中监听 ScenePhase

  • @Environment(\.scenePhase)
    • 状态
      • .active:应用在前台且可交互。
      • .inactive:应用在前台但不可交互(如拉下通知栏时)。
      • .background:应用进入后台。
plaintext
struct ContentView: View {
    @Environment(\.scenePhase) var scenePhase

    var body: some View {
        Text("Hello")
            .onChange(of: scenePhase) { newPhase in
                switch newPhase {
                case .active:
                    print("应用处于活跃状态")
                case .background:
                    print("应用进入后台")
                case .inactive:
                    print("应用非活跃")
                @unknown default:
                    break
                }
            }
    }
}

5. 初始化阶段 (init)

虽然 init 是结构体的一部分,但不建议init 中进行繁重的逻辑处理或副作用操作(如网络请求)。

  • 触发时机:视图结构体被创建时。
  • 注意:SwiftUI 会非常频繁地创建 View 的实例来计算 Diff(差异),即使视图并没有在屏幕上更新。
  • 用途:仅用于配置视图的初始属性、初始化 State(如果必须)。

总结对比表

SwiftUI 方法 类似于 UIKit 的 最佳用途
init() init 配置参数,不要做耗时操作
onAppear viewDidAppear 同步设置,简单的 UI 逻辑
task viewDidAppear (支持 Async) 网络请求,异步加载,自动取消
onDisappear viewDidDisappear 清理资源,停止定时器
onChange KVO / Delegate 响应数据模型的变化
ScenePhase AppDelegate / SceneDelegate 监听 App 进入后台/前台

最佳实践建议

  1. 首选 .task 而不是 .onAppear:如果你需要加载数据,使用 .task,因为它集成了 Swift 并发模型且能自动处理取消,防止内存泄漏。
  2. 不要在 init 中做逻辑:永远假设 init 会被调用无数次。将逻辑放在 onAppeartask 中。
  3. 理解惰性加载:在 NavigationStackTabView 中,目标视图的 init 可能很早就执行了,但 onAppear 只有在用户真正看到该页面时才会执行。
00:00
00:00