SwiftUI 视图的生命周期方法有哪些?
SwiftUI 的视图(View)本质上是结构体(Struct),是值类型。这意味着它们会被频繁地创建和销毁。因此,SwiftUI 的生命周期管理与 UIKit(基于类和引用类型)有很大不同。
SwiftUI 不再依赖 viewDidLoad 这种控制器级别的方法,而是通过修饰符(Modifiers)来响应视图在层级结构中的变化。
以下是 SwiftUI 中核心的生命周期方法:
1. 视图出现与消失 (Appear / Disappear)
这是最常用的两个生命周期修饰符,对应 UIKit 的 viewWillAppear/viewDidAppear 和 viewWillDisappear/viewDidDisappear。
.onAppear(perform:)- 触发时机:当视图被插入到视图层级结构中并显示在屏幕上时。
- 用途:开始动画、请求数据(如果不需要 async/await)、埋点统计。
- 注意:在
List或LazyVStack中,滚动导致视图复用时会重复触发。
.onDisappear(perform:)- 触发时机:当视图从视图层级结构中移除时。
- 用途:停止动画、取消不再需要的操作、保存临时数据。
plaintext
Text("Hello World")
.onAppear {
print("视图出现了")
}
.onDisappear {
print("视图消失了")
}
2. 异步任务 (Task) - iOS 15+
这是现代 SwiftUI 开发中替代 onAppear 进行异步操作的首选方式。
.task(priority:action:)- 触发时机:与
onAppear类似,当视图出现时触发。 - 特点:
- 支持 Swift Concurrency (
async/await)。 - 自动取消:当视图消失(触发
onDisappear)时,该 Task 会自动被取消。
- 支持 Swift Concurrency (
- 用途:网络请求、加载重型数据。
- 触发时机:与
plaintext
Text("Loading Data...")
.task {
// 这里可以写 await 代码
await loadData()
} // 视图消失时,loadData 会自动收到取消信号
.task(id:priority:action:)- 变体:当指定的
id值发生变化时,会取消旧任务并重新运行新任务。类似于onChange+task的结合。
- 变体:当指定的
3. 状态变化 (Change)
SwiftUI 是数据驱动的,因此监听数据变化也是生命周期的一部分。
.onChange(of:perform:)- 触发时机:当指定的
State、Binding或Environment值发生变化时。 - 用途:根据数据变化执行逻辑(如表单验证、联动更新)。
- 触发时机:当指定的
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 进入后台/前台 |
最佳实践建议
- 首选
.task而不是.onAppear:如果你需要加载数据,使用.task,因为它集成了 Swift 并发模型且能自动处理取消,防止内存泄漏。 - 不要在
init中做逻辑:永远假设init会被调用无数次。将逻辑放在onAppear或task中。 - 理解惰性加载:在
NavigationStack或TabView中,目标视图的init可能很早就执行了,但onAppear只有在用户真正看到该页面时才会执行。