前言
在 iOS 17 的操作系统上,Apple 的 SwiftUI 框架在处理数据的方式上做了一些改变:其推出了 @ObservableObject
和 @StateObject
的 Combine 版本,它采用了 @Observable
宏(这是一个名为Observation
的新包的一部分)的形式。
一个有趣的新增功能是 @Bindable
属性包装器。这个属性包装器与 SwiftUI 中的 @Binding
共存,它们的组合使用可以允许开发人员创建到可观察类属性的绑定。那么这些属性包装器的作用是什么呢?是什么使它们彼此不同?下面我们在接下来的内容中一起梳理一下。
@Binding
首先,我们来看一下 @Binding
属性包装器。当我们需要一个视图来改变另一个视图拥有的数据时,我们会使用 @Binding
创建一个绑定。例如,我们的绑定可以是这样的:
css
struct MyButton: View {
@Binding var count: Int
var body: some View {
Button(action: {
count += 1
}, label: {
Text("加 1")
})
}
}
这个例子并没有什么特殊之处,但它说明了我们如何编写一个视图来读取和改变这个视图外部拥有的计数器。
数据所有权在 SwiftUI
中是一个非常重要的话题,它的属性包装器可以真正帮助我们理解谁拥有什么。在@Binding
的情况下,我们所知道的是:其他视图将为我们提供读取计数的能力,以及改变该计数器的方法。
每当用户点击 MyButton
时,计数器就会增加,视图也会更新。这包括最初拥有并使用该计数器的视图。
在 SwiftUI 中,绑定经常用于开箱即用的组件中。例如,TextField
需要绑定到视图拥有的 String
属性。这允许 TextField
读取视图拥有的值,并且 TextField
还可以根据用户的输入更新文本值。
那 @Bindable
又是用来做什么的呢?
@Bindable
如果你比较熟悉 iOS16
或更早版本的 SwiftUI
,你会知道你可以创建绑定到 @State
, @StateObject
, @ObservedObject
和更多类似的对象。在 iOS 17 中,我们可以访问 @Observable
宏,但它不能像 ObservableObject
那样创建绑定。相反,如果我们的 @Observable
对象是一个类,我们可以让视图把这个对象设置为可绑定的。
这意味着我们可以用 @Bindable
属性包装器标记一个包含 Observable
类实例的属性,允许我们创建到类实例属性的绑定。如果没有 @Bindable
,我们就做不到:
swift
@Observable
class MyCounter {
var count = 0
}
struct ContentView: View {
var counter: MyCounter = MyCounter()
init() {
print("init")
}
var body: some View {
VStack {
Text("The counter is \(counter.count)")
// 编译报错:Cannot find '$counter' in scope
MyButton(count: $counter.count)
}
.padding()
}
}
当我们将 var counter
属性设置为 @Bindable
时,我们可以创建一个绑定到计数器的 count 属性:
swift
@Observable
class MyCounter {
var count = 0
}
struct ContentView: View {
@Bindable var counter: MyCounter
init() {
print("init")
}
var body: some View {
VStack {
Text("The counter is \(counter.count)")
MyButton(count: $counter.count)
}
.padding()
}
}
需要注意的是,如果你的视图拥有 Observable
对象,你通常会用 @State
标记它,并在视图中创建对象实例。当你的 Observable
对象被标记为 @State
时,你就可以创建对该对象属性的绑定。这要归功于 @State
属性包装器注释。
然而,如果你的视图不拥有 Observable
对象,那么使用 @State
就不合适了。创建 @Bindable
属性包装器就是为了解决这种情况,它允许你创建到对象属性的绑定。
Bindable
的使用仅限于符合 Observable
协议的类。创建一个符合 Observable
的对象最简单的方法是使用 @Observable
宏。
总结
在这篇文章中,你了解到 @Binding
和 @Bindable
的主要区别在于它们的作用:
@Binding
属性包装器表明视图上的某些状态块由另一个视图拥有,并且你可以对底层数据进行读写访问。@Bindable
属性包装器允许你为Observable
类拥有的属性创建绑定。如前所述,@Bindable
仅限于符合Observable
的类,而创建Observable
对象最简单的方法是使用@Observable
宏。