SwiftUI — Bindable

Bindable #

在 SwiftUI 中,@Bindable 是一个属性包装器(Property Wrapper),用于创建可绑定的状态。它通常与 @Observable 结合使用,允许你将模型数据与视图绑定,从而实现数据的双向绑定。


什么是 @Bindable #

@Bindable 是 SwiftUI 提供的一个属性包装器,用于将 Observable 对象中的属性绑定到视图控件(如 TextFieldToggle 等)。通过 @Bindable,你可以轻松实现视图与数据模型之间的双向绑定。


使用场景 #

  • 当你有一个 Observable 对象,并且希望将其属性绑定到视图控件时。
  • 当你需要在子视图中修改父视图传递的 Observable 对象时。

基本用法 #

1. 定义 Observable 模型 #

首先,你需要定义一个符合 Observable 协议的模型类。Observable 是 SwiftUI 提供的一个协议,用于标记可观察的对象。

import SwiftUI
import Observation

@Observable
class User {
    var name: String = ""
    var isSubscribed: Bool = false
}

2. 在视图中使用 @Bindable #

在视图中,你可以使用 @BindableObservable 对象的属性绑定到视图控件。

struct ContentView: View {
    @State private var user = User()

    var body: some View {
        VStack {
            TextField("Enter your name", text: $user.name)
                .textFieldStyle(.roundedBorder)
                .padding()

            Toggle("Subscribe", isOn: $user.isSubscribed)
                .padding()

            Text("Hello, \(user.name)!")
            Text(user.isSubscribed ? "Subscribed" : "Not Subscribed")
        }
        .padding()
    }
}

在这个例子中:

  • @State 用于创建并管理 User 对象。
  • @Bindable 隐式地用于 $user.name$user.isSubscribed,将 User 对象的属性绑定到 TextFieldToggle

在子视图中使用 @Bindable #

如果你需要将 Observable 对象传递给子视图,并在子视图中修改它的属性,可以使用 @Bindable

示例代码 #

struct ContentView: View {
    @State private var user = User()

    var body: some View {
        VStack {
            Text("Parent View")
            TextField("Enter your name", text: $user.name)
                .textFieldStyle(.roundedBorder)
                .padding()

            SubView(user: user)
        }
        .padding()
    }
}

struct SubView: View {
    @Bindable var user: User

    var body: some View {
        VStack {
            Text("Sub View")
            Toggle("Subscribe", isOn: $user.isSubscribed)
                .padding()

            Text("Hello, \(user.name)!")
            Text(user.isSubscribed ? "Subscribed" : "Not Subscribed")
        }
        .padding()
    }
}

在这个例子中:

  • ContentViewUser 对象传递给 SubView
  • SubView 使用 @Bindable 来绑定 User 对象的属性,并可以在子视图中修改这些属性。

@Bindable 的工作原理 #

  • @Bindable 会创建一个对 Observable 对象的绑定引用。
  • Observable 对象的属性发生变化时,视图会自动更新。
  • 当视图控件(如 TextFieldToggle)修改绑定的属性时,Observable 对象的值也会同步更新。

@State@Binding 的区别 #

特性@State@Binding@Bindable
作用对象视图的私有状态父视图传递的状态绑定Observable 对象的绑定
使用场景管理视图内部的状态在子视图中修改父视图的状态绑定 Observable 对象的属性
是否可写
是否支持复杂对象是(但需要手动实现 Observable是(但需要手动实现 Observable是(直接支持 Observable 对象)

总结 #

  • @Bindable 是 SwiftUI 中用于绑定 Observable 对象属性的工具。
  • 它通常与 Observable 协议一起使用,实现视图与数据模型之间的双向绑定。
  • 你可以在父视图中使用 @State 创建 Observable 对象,并通过 @Bindable 将其传递给子视图。
  • @Bindable 使得在 SwiftUI 中处理复杂数据绑定变得更加简单和直观。

通过 @Bindable,你可以更轻松地实现视图与数据模型之间的双向绑定,从而构建出更动态和响应式的用户界面。

在 SwiftUI 中,BindableBinding 都与数据的双向绑定(data binding)相关,但它们用于不同的场景和目的。让我们详细地探讨一下它们的差异和使用场景。

区别 #

1. Binding #

Binding 是一种 引用类型,它允许你在视图中引用某个数据源并绑定到该数据源。这意味着视图会与某个状态共享数据,并且该数据可以在视图和状态之间双向传递。通过 Binding,当视图中的数据发生变化时,它会自动更新原始数据。

用法场景: #

  • 传递数据:当你需要将某个数据(通常是 @State@ObservedObject 中的数据)传递给子视图,并允许子视图修改该数据时,你使用 Binding
  • 双向绑定:如果你希望子视图能够修改父视图的数据,Binding 是适合的选择。

例子: #

struct ParentView: View {
    @State private var value = false
    
    var body: some View {
        ChildView(isEnabled: $value)
    }
}

struct ChildView: View {
    @Binding var isEnabled: Bool
    
    var body: some View {
        Toggle(isOn: $isEnabled) {
            Text("Enable Feature")
        }
    }
}

在这个例子中:

  • ParentView 中定义了一个 @State 属性 value
  • ParentView 将这个状态通过 Binding 传递给了 ChildView,使得 ChildView 可以修改 value

2. BindableObject(或 Bindable #

BindableObject 是一个协议,它用来让你创建可以绑定到视图的数据模型,通常在更复杂的数据结构和状态管理中使用。BindableObject 用于 类型,而 @Binding 用于结构体类型(通常用于视图的状态管理)。它允许你通过 @Published 标记的属性发布变化,并通知观察者(通常是 SwiftUI 视图)以便更新。

用法场景: #

  • 在模型中共享状态:当你需要将数据从一个视图传递到多个视图时,BindableObject(或 ObservableObject)是合适的。它让你能够创建一个数据模型类,并通过 @Published 标记其属性,使其能够绑定到视图中。
  • 视图间共享数据:适用于需要在多个视图之间共享同一份数据的情况,通常在大型应用或多个组件需要共享同一份数据时使用。

例子: #

class Settings: ObservableObject {
    @Published var isEnabled: Bool = false
}

struct ContentView: View {
    @StateObject var settings = Settings()
    
    var body: some View {
        Toggle("Enable Feature", isOn: $settings.isEnabled)
    }
}

在这个例子中:

  • Settings 类是一个 ObservableObject,它包含一个 @Published 属性 isEnabled,表示是否启用某个功能。
  • ContentView 使用 @StateObject 来创建并管理 Settings 实例。通过 @BindingToggle 组件与 settings.isEnabled 绑定。

3. 总结:什么时候使用 BindingBindableObject(或 ObservableObject #

  • 使用 Binding

    • 当你需要将父视图中的状态传递给子视图,并允许子视图修改该状态时,使用 Binding
    • 适用于简单的视图状态绑定,通常用于局部视图中的状态传递。

    场景:在父视图中有 @State,并希望将其状态传递到子视图中供修改。

    例子

    • 绑定一个 ToggleTextField 的输入到父视图的状态。
  • 使用 BindableObject(或 ObservableObject

    • 当你有一个复杂的数据模型,并且希望在多个视图之间共享状态时,使用 BindableObject(通常是 ObservableObject)。
    • 适用于多个视图需要共享数据,并且这个数据可能发生变化时,适合用 ObservableObject 来管理。

    场景:在一个大的视图层级中,需要共享一个全局状态(如用户设置、应用配置等),并且这个状态的更新需要通知多个视图。

    例子

    • 在大型应用中,管理应用级设置或用户信息等,通过 @Published 标记的属性通知视图更新。

4. 对比总结 #

特性BindingObservableObject (BindableObject)
类型适用于结构体(如 @State)。适用于类(如 @StateObject@ObservedObject)。
数据的所有者通常在父视图中管理状态。数据模型(类)管理状态,视图订阅该数据。
作用范围用于父视图与子视图之间的双向数据绑定。用于跨多个视图共享和更新数据。
修改数据的权限子视图可以修改父视图的状态。视图可以读取和修改模型数据。
适用场景简单的局部视图状态传递。管理跨多个视图的数据,适用于复杂状态管理。

小结: #

  • 使用 Binding 是为了将状态从父视图传递到子视图并允许子视图修改它,适合简单的视图层级。
  • 使用 ObservableObject(或 BindableObject)是为了管理一个全局的数据模型,并在多个视图间共享和更新它,适合大型应用或多个视图需要访问和修改同一份数据时。
本文共 2611 字,上次修改于 Feb 4, 2025