参考:
https://blog.csdn.net/weixin_45727359/article/details/109108544
https://zhuanlan.zhihu.com/p/141229504?from_voters_page=true
在SwiftUI中,以单一数据源(single source of truth)为核心,构建了数据驱动状态更新的机制。其中引入了多种新的属性包装器(property wrapper),用来进行状态管理。
属性包装器(property wrapper):@State、@ObservedObject、@EnvironmentObject
@State
用于单一视图的本地状态,
@ObservedObject
从一个视图传递到另一个视图。
@EnvironmentObject
比它们都更进一步:可以把一个对象注入环境,以便任何子视图都可以自动获得该对象的访问能力
@State
struct User {
var firstName = "Bilbo"
var lastName = "Baggins"
}
struct ContentView: View {
@State private var user = User() //1
var body: some View {
VStack {
Text("Your name is \(user.firstName) \(user.lastName).") //2
TextField("First name", text: $user.firstName) //3
TextField("Last name", text: $user.lastName)
}
}
}
@State是一个属性包装器(property wrapper),被设计用来针对值类型进行状态管理;用于在Struct中mutable值类型
- 对于 @State 修饰的属性的访问,只能发生在 body 或者 body 所调用的方法中。
- 如果是读写都有,引用属性需要$开头,如果只读直接使用变量名即可
@Binding
@Binding的作用是在保存状态的属性和更改数据的视图之间创建双向连接
将当前属性连接到存储在别处的单一数据源(single source of truth)
struct Product:Identifiable { var isFavorited:Bool var title:String var id: String } struct FilterView: View { @Binding var showFavorited: Bool //3 var body: some View { Toggle(isOn: $showFavorited) { //4 Text("Change filter") } } } struct ProductsView: View { let products: [Product] = [ Product(isFavorited: true, title: "ggggg",id: "1"), Product(isFavorited: false, title: "3333",id: "2")] @State private var showFavorited: Bool = false //1 var body: some View { List { FilterView(showFavorited: $showFavorited) //2 ForEach(products) { product in if !self.showFavorited || product.isFavorited { Text(product.title) } } } } }
@StateObject 和 @ObservedObject
@StateObject 和 @ObservedObject 的区别就是实例是否被创建其的View所持有,其生命周期是否完全可控。
@ObservedObject创建的实例生命周期可能长于/短于/等于当前的View的生命周期
三段代码,三种结果,这也就是为什么苹果要新增@StateObject的原因——让开发者可以明确地了解并掌握实例的生命周期,消除不确定性!
苹果使用@StateObject一方面修复了之前的隐患,同时通过SwiftUI2.0众多新特性的引入,进一步完善了Data Flow的实现手段。
SwiftUI 2.0 —— @StateObject 研究
class User: ObservableObject { @Published var firstName = "Bilbo" @Published var lastName = "Baggins" } struct ContentView: View { @ObservedObject private var user = User() //@ObservedObject
属性包装器只能用于符合ObservableObject
协议的类型 var body: some View { VStack { Text("Your name is \(user.firstName) \(user.lastName).") TextField("First name", text: $user.firstName) TextField("Last name", text: $user.lastName) } } }
@EnvironmentObject
@EnvironmentObject
属性包装器是说明属性的数据是来自环境,而不是在本地创建的:
class User: ObservableObject { @Published var name = "Taylor Swift" } struct EditView: View { @EnvironmentObject var user: User var body: some View { TextField("Name", text: $user.name) } } struct DisplayView: View { @EnvironmentObject var user: User var body: some View { Text(user.name) } } struct ContentView: View { let user = User() var body: some View { VStack { EditView().environmentObject(user) DisplayView().environmentObject(user) } } }
把 ContentView
改成下面这样:你会发现结果一样。我们现在是把 user
放到 ContentView
的环境中,但是因为 EditView
和 DisplayView
都是 ContentView
的子视图,所以它们自动继承了 ContentView
的环境。
VStack { EditView() DisplayView() } .environmentObject(user)