使用 @Observable 和 @Environment 是 SwiftUI 最新的状态管理机制,它引入了更简洁、类型安全的方式来共享和观察数据。
什么是 @Observable?
@Observable 是 Swift 宏,用于将类声明为可观察对象。它会自动生成必要的代码,使该类可以被 SwiftUI 自动观察,从而触发视图更新。
示例
@Observable
class Person {
var name = "Fangjunyu"
var age = 27
}
@Observable 会自动生成代码,使 Person 类的属性成为可观察的。
当 Person 的属性值发生变化时,绑定到这些属性的视图会自动刷新。
使用 @Environment 共享数据
@Environment 是 SwiftUI 提供的机制,用于在视图层次结构中共享数据对象,避免逐层传递。结合 @Observable,可以轻松实现这种共享。
完整示例
定义可观察对象
@Observable
class Person {
var name = "Fangjunyu"
var age = 27
}
将对象放入环境中
使用 .environment(person) 将 Person 对象注入视图的环境中:
struct ContentView: View {
@State private var person = Person() // 创建可观察对象
var body: some View {
VStack {
PeopleList()
}
.environment(person) // 将对象放入环境
}
}
在子视图中读取环境对象
使用 @Environment(Person.self) 从环境中读取对象:
struct PeopleList: View {
@Environment(Person.self) var person // 从环境中读取 Person
var body: some View {
VStack {
Text("Person: \(person.name)")
Text("Age: \(person.age)")
}
}
}
现在,PeopleList 自动从环境中获取 Person 对象,并响应其属性的变化。
数据绑定:与 @Bindable 一起使用
@Environment 中读取数据并不支持直接双向绑定。因为@Environment 只允许只读访问,不能直接通过它进行写操作。
因此,应该考虑使用@EnvironmentObject。
和传统 @EnvironmentObject 的对比
运行时注意事项
环境中必须存在对象:
如果一个视图声明了 @Environment(Person.self),但在父视图中未通过 .environment(person) 注入该类型对象,应用会崩溃。
解决方法:确保在父视图中正确使用 .environment(person)。
注意:这个问题也会发生在预览中,因此需要在子视图的预览中使用:
#Preview {
ResortView(resort: Resort.example)
.environment(Favorites())
}
让SwiftUI预览继续工作。
双向绑定问题:
目前(iOS 17),@Environment 的对象属性不能直接绑定。
总结
@Observable 和 @Environment 提供了一种现代、类型安全且简洁的状态管理方式。
适用场景:
需要局部共享的轻量级数据。
希望避免使用传统的 ObservableObject 和 Combine。
局限性:
当前不支持直接绑定,可能在未来的 iOS 版本中改进。
这种机制可以使代码更简洁,更符合 SwiftUI 的声明式编程风格,尤其适合构建复杂的应用数据共享场景。
相关文章
1、Swift @Observable属性包装器:https://fangjunyu.com/2024/11/03/swift-observable%e5%b1%9e%e6%80%a7%e5%8c%85%e8%a3%85%e5%99%a8/
2、Sharing @Observable objects through SwiftUI’s environment:https://www.hackingwithswift.com/books/ios-swiftui/sharing-observable-objects-through-swiftuis-environment