SwiftUI 中一些和响应式状态有关的属性包装器的用途

两者的区别是:

@State: 主要用于修饰值类型那种简单属性。
@StateObject: 和 @ObservedObject 一样,主要用于引用类型那种复杂属性。
举例说明。在一个 SwiftUI View 中声明属性:

@State var name: String
那么,每次 name 发生变化时,View 都会重新渲染。

但假如有一个类:

class Student {
var name: String = “”
}
当它的实例被用 @State 修饰时:

@State var student: Student = Student()
则 View 不会随着 student.name 的变化而变化,因为实例 student 本身并没有发生变化。为了让 View 随 student 的属性变化,就要用到 @StateObject 来修饰:

@StateObject var student: Student = Student()
同时还要给 Student 的属性添加 @Published 修饰:

class Student: ObservableObject {
@Published var name: String = Student()
}
如此,每次 student.name 发生变化时,View 就会随之重新渲染了。

@Binding 和 @ObjectBinding
有时候我们在子 View 中需要用到父 View 的属性,并且不仅仅单方面的显示,还要有双向的影响,即子 View 对属性的更改,能反应到父 View 上。

在以下情况下:

// Parent View
struct ForumView: View {
@State var username: String = “”

var body: some View {
    Text(username)
    InputView(name: username)
}

}

// Child View
struct InputView: View {
@State var name: String = “”

var body: some View {
    Text(name)
    Button(action: {
        name = "Tom"
    }) {
        Text("Change Name")
    }
}

}

// Preview: ForumView(username: “Jack”)
父 View 虽然能把自己属性的值传给子 View,但是子 View 在改变其属性值时,仅能够改变它自身,而不能影响到父 View。若要影响到父 View,就需要用到 @Binding 了:

// Parent View
struct ForumView: View {
@State var username: String = “”

var body: some View {
    Text(username)
    InputView(name: $username)
}

}

// Child View
struct InputView: View {
@Binding var name: String

var body: some View {
    Text(name)
    Button(action: {
        name = "Tom"
    }) {
        Text("Change Name")
    }
}

}

// Preview: ForumView(username: “Jack”)
此时,你通过子 View 改变的值 (name),就同时也能改变到父 View 的属性 (username) 了。

简而言之,@Binding 就是对其他属性的一种引用式的绑定。注意用法:它在声明时,不需要赋初始值,在用到时,要加前缀 $ 。

@Binding 对应 @State,则 @ObjectBinding 便对应 @ObservedObject 和 @StateObject 了,毋庸赘言。

@ObservedObject 和 @StateObject
用 @ObservedObject 和 @StateObject 包装的属性都需要其对象类实现 ObservableObject 协议。本质上,他们都是用来让对象状态化的包装器。但在使用时,有一定区别。

简单地说,@ObservedObject 会在 View 每次被重新渲染时重新构造,它包装的 Model 是跟着 View 走的,而 @StateObject 则不会,它一旦被创建,就由 SwiftUI 接管,不会随着 View 的刷新渲染而重建。

为什么会这样,因为 View 作为 struct 是一个值类型的对象,他被销毁时,它内部的对象也会被销毁,而 @StateObject 等于是给 View 内部的对象加了一层保护,使其不受 View 生命周期的影响。

有时我们通过 NavigationView 来回切换页面,会发现 @StageObject 对象也被重置了,像是随着 View 刷新而重建一样,其实那是 SwiftUI 的行为。

比较而言,我觉得 @StateObject 更好,因为它和 View 解耦了,更方便控制。

@EnvironmentObject
@EnvironmentObject 有 @StateObject 那种脱离 View 生命周期的特性,但在使用上更为灵活。举例来说:

View A: 创建了 @StateObject var thing: Thing,包含 View B
View B: 包含 View C
View C: 需要用到 View A 的 thing 对象。
一般来说,为了让 View C 用到 View A 的 thing,就需要从 View A 开始传递 thing 给 View B, 再由 View B 传给 View C 使用。这是不是太麻烦了,View B 凭空多了一个它用不到但却能访问的对象 thing。@EnvironmentObject 的存在就是为了解决这个问题。

在 View A 中:

var thing: Thing = Thing(tag: “e”)
var body: some View {
NavigationView {
ViewB()
}.environmentObject(thing)
}
通过 .environmentObject(), thing 变成了环境对象。接下来我们在 View C 中就可以直接使用了:

@EnvironmentObject var thing: Thing
var body: some View {
// thing.tag: “e”
Text(thing.tag)
}
可以看到 View C 中 @EnvironmentObject var thing: Thing 不用初始化 thing,因为这个 thing 就是 ViewA 中的 thing。EnvironmentObject 就像把一个对象全局化了一样。
深圳网站建设www.sz886.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值