SwiftUI 中 @Binding 和 @Bindable 的区别

前言

在 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 宏。
相关推荐
大熊猫侯佩19 小时前
SwiftUI 中无法对添加模糊(blur)效果视图截图的初步解决
swiftui·swift·apple
大熊猫侯佩2 天前
Xcode 15.0 新 #Preview 预览让 SwiftUI 界面调试更加悠然自得
swiftui·swift·apple
东坡肘子3 天前
WWDC 2025:回归务实的一年 | 肘子的 Swift 周报 #089
swiftui·swift·wwdc
大熊猫侯佩3 天前
SwiftUI 5.0(iOS 17.0,macOS 14.0+)新 Inspector 辅助视图之趣味漫谈
macos·ios·swiftui
大熊猫侯佩4 天前
SwiftUI 调整视图内容周围间隙(Content Margins)的“时髦”方法
swiftui·swift·apple
I烟雨云渊T5 天前
iOS swiftUI的实用举例
ios·swiftui·swift
大熊猫侯佩5 天前
SwiftUI 中为何 DisclosureGroup 视图在收缩时没有动画效果?
swiftui·swift·apple