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 宏。
相关推荐
大熊猫侯佩1 天前
SwiftUI 三阵诀:杨过绝情谷悟 “视图布阵” 之道
swiftui·swift·apple
东坡肘子3 天前
未来将至:人形机器人运动会 | 肘子的 Swift 周报 #099
swiftui·swift·apple
吴Wu涛涛涛涛涛Tao5 天前
基于TCA构建Instagram克隆:SwiftUI状态管理的艺术
ios·swiftui
麦兜*8 天前
Swift + Xcode 开发环境搭建终极指南
开发语言·ios·swiftui·xcode·swift·苹果vision pro·swift5.6.3
大熊猫侯佩10 天前
「内力探查术」:用 Instruments 勘破 SwiftUI 卡顿迷局
swiftui·debug·xcode
HarderCoder10 天前
深入理解 SwiftUI 的 ViewBuilder:从隐式语法到自定义容器
swiftui·swift
东坡肘子10 天前
我差点失去了巴顿(我的狗狗) | 肘子的 Swift 周报 #098
swiftui·swift·apple
黄鹤的小姨子12 天前
SwiftUI 劝退实录:AI 都无能为力,你敢用吗?
swiftui
麦兜*12 天前
【swift】SwiftUI动画卡顿全解:GeometryReader滥用检测与Canvas绘制替代方案
服务器·ios·swiftui·android studio·objective-c·ai编程·swift
东坡肘子17 天前
苹果首次在中国永久关闭了一家 Apple Store | 肘子的 Swift 周报 #097
swiftui·swift·apple