@Binding — 是什么,如何工作及 SwiftUI 示例

作者: IT Sectr 发布日期: 2026-06-19 阅读时间: 7 分钟

@Binding 是 SwiftUI 中的一个属性包装器,用于创建对另一个组件所拥有数据的引用。Binding 不独立存储值——它仅通过 $ 投影提供对现有真实数据源的访问。根据 Apple Developer Documentation (2025),Binding 在父视图和子视图之间提供响应式双向连接,而不直接拥有数据。@Binding 是沿层次结构向下传递可变状态的关键机制。

要点

  • @Binding — 用于创建对父视图状态引用的属性包装器
  • 无所有权 — Binding 不存储数据,仅提供对真实数据源的访问
  • $ 投影 — $stateValue 从 @State 或 @StateObject 创建 Binding
  • 双向连接 — 子视图中的更改立即反映在父视图中
  • Binding.constant — 用于无反馈的原型设计的固定值

什么是 SwiftUI 中的 @Binding?

@Binding 是一个属性包装器,它在存储于父视图中的属性和子组件之间创建双向连接。Binding 与 @State 的主要区别:Binding 不拥有数据。它只通过真实源——@State、@StateObject 或父视图中的其他 Binding——读取和写入值。没有 Binding,子视图将无法在没有回调或委托的情况下修改祖先的状态。

Binding 实现为一个具有两个属性的结构:wrappedValue(当前值)和 projectedValue(Binding 本身,可通过 $ 访问)。当子视图通过 Binding 更改 wrappedValue 时,SwiftUI 将更改传输到数据源并重新绘制所有依赖的视图。这在当前更新周期内同步发生。

重要特性:@Binding 不仅限于单层传递。Binding 可以通过层次结构的多个级别传递——每个子组件都接收到对同一数据源的引用。任何级别的更改都会导致所有关联视图的统一更新。

Binding 的双向连接如何工作

通过 @Binding 的双向连接机制基于属性包装器的投影构建。当父视图声明 @State var value: T 时,SwiftUI 会自动生成类型为 Binding 的 $value 投影。通过将 $value 传递给具有 @Binding var value: T 的子组件,您将两个视图连接到一个内存单元。子视图中通过 Binding 进行的任何写入都会导致两个组件的重新绘制。

swift
struct SliderContainer: View {
    @State private var value: Double = 0.5

    var body: some View {
        VStack {
            Text("值: \(value)")
            SliderView(value: $value)
        }
    }
}

struct SliderView: View {
    @Binding var value: Double

    var body: some View {
        Slider(value: $value, in: 0...1)
    }
}

在示例中,SliderContainer 拥有 @State value,而 SliderView 通过 $value 接收 Binding。SliderView 内部的滑块绑定到此 Binding。拖动滑块时,Slider 通过 Binding 更改值,这会自动更新 SliderContainer 中的 @State,并且两个视图都显示当前数字。整个链无需任何回调或通知即可工作。

要从 @StateObject 或 @ObservedObject 创建 Binding,使用相同的投影:$object.property 提供 Binding。这允许将 ObservableObject 的单独属性传递给子视图,而无需传递整个对象。这种方法提供更紧密的绑定并防止不必要的重新绘制。

@Binding 与回调:如何选择

在 SwiftUI 之前,向上传递层次结构变化的标准方式是通过 回调和委托:父视图传递一个闭包,子组件在更改时调用它。@Binding 提供了一种代码更少、语法更声明式的替代方案。无需传递完成闭包,只需传递 $stateValue 即可。

标准@Binding回调
代码一个注解 + $闭包 + 调用
多层自动闭包链
测试Binding(value:constant)模拟闭包
可读性
灵活性仅数据任何逻辑

当子视图只需要读取和更改值时,使用 @Binding。如果更改时需要副作用(验证、日志记录、网络请求),将 Binding 与回调结合使用:传递 Binding 用于数据,传递闭包用于事件。例如,TextField 可以绑定到 Binding,而 onChange 将触发验证。

项目中 @Binding 的使用模式

@Binding 应用于几种典型场景。第一——自定义控件:开关、滑块、颜色选择器和其他交互元素接受 Binding 以进行双向同步。第二——模态窗口:sheet 显示标志作为 Binding 传递,允许子视图通过 presentationMode 或直接设置关闭自身。

第三种模式——拆分表单。如果表单由多个字段组成,每个字段可以提取到接受其值的 Binding 的单独组件中。这简化了不同表单之间字段的测试和重用。父组件仍然是整个表单模型的唯一所有者。

swift
struct FormField: View {
    let title: String
    @Binding var text: String

    var body: some View {
        VStack(alignment: .leading) {
            Text(title).font(.caption)
            TextField("Enter \(title.lowercased())", text: $text)
                .textFieldStyle(.roundedBorder)
        }
    }
}

FormField 组件接受标签和字符串的 Binding。它显示标题和绑定到传递的 Binding 的 TextField。任何表单都可以通过为每个字段传递 $property 来多次使用 FormField。这减少了标记的重复并集中了文本字段的样式设置。

创建自定义 Binding

SwiftUI 允许通过 Binding(get:set:) 初始化器手动创建 Binding。当需要在读取或写入值时添加逻辑时,这很有用。例如,您可以创建一个在保存前格式化数字的 Binding,或者一个在每次更改时与远程服务器同步值的 Binding。

swift
struct ValidatedField: View {
    @State private var email: String = ""

    var emailBinding: Binding<String> {
        .init(
            get: { email },
            set: { email = $0.lowercased().trimmingCharacters(in: .whitespaces) }
        )
    }

    var body: some View {
        TextField("Email", text: emailBinding)
    }
}

在列表中,自定义的 emailBinding 在每次更改时自动将文本转换为小写并移除空格。TextField 使用此 Binding 而不是直接绑定到 $email。这种方法允许在 Binding 内部集中验证和数据转换,而不会用 onChange 处理程序污染代码。

@Binding 的错误和反模式

第一个也是最常见的错误——传递值而不是 Binding。如果子组件声明 @Binding var text: String,而父视图传递 text(不带 $),编译器将给出错误:Cannot convert value of type 'String' to expected argument type 'Binding'。解决方案——传递时始终使用 $ 前缀:$text。

第二个错误——对只读数据的 Binding。如果子视图只需要读取值,不要使用 @Binding——普通的 let 或来自父视图的 @State 就足够了。Binding 暗示写入可能,额外的修改权限使调试复杂化并违反最小权限原则。

第三个问题——在生产中使用 Binding.constant。Binding.constant(value) 创建没有反馈的虚假绑定——更改被忽略。仅将 constant 用于原型设计和预览(Xcode Previews),但绝不要在真实代码中使用。对于测试,使用具有受控行为的 Binding(get:set:)。

常见问题

@Binding 和 @State 有什么区别?

@State 拥有数据并管理其在堆中的存储。@Binding 仅引用现有状态而不拥有。@State 始终是 private,@Binding 是子视图的输入参数。

可以在没有 @State 的情况下创建 Binding 吗?

可以,通过 Binding(get:set:) 初始化器或 Binding.constant(value)。Binding 也可以从 @StateObject 通过 $object.$property 投影获取,以及从 Publisher 通过 Subscribe 内的 Binding(get:set:) 获取。

如何通过多个嵌套级别传递 Binding?

@Binding 沿链传递:每个中间组件声明 @Binding 并通过 $ 进一步传递。所有级别引用根视图中的一个数据源。

为什么 Binding.constant 不更新界面?

Binding.constant 创建一个静默包装器——setter 忽略新值。它仅用于原型设计和 SwiftUI 预览,其中不需要来自子组件的反馈。

@Binding 可以是可选的?

是的,支持 Binding<T?>。如果您传递 Binding<String?>,子视图可以设置 nil。这对于可选的表单字段或具有重置可能性的状态很方便。

总结

  • @Binding — 用于与父视图数据双向连接的属性包装器
  • 不拥有数据——仅提供对真实数据源的访问
  • $ 投影 将 @State、@StateObject 转换为 Binding 以传递给子视图
  • 自定义 Binding 通过 Binding(get:set:) 创建,带有额外逻辑
  • Binding.constant — 仅用于预览和原型
  • 多层传递——Binding 通过任意嵌套深度
  • 回调的替代方案——从子组件修改状态的声明式方式

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读