我正在学习使用Swift和SwiftUI进行iOS编程。 我所知甚少,对于@State
和@Binding
之间的区别非常困惑。
如果我理解正确,@Binding
本质上就是@State
,但它不会更新视图。 如果确实如此,那么既然可以使用@State
来完成相同的事情,为什么还需要@Binding
呢?
@State
和@Binding
都是属性包装器。
@State
@Binding
@ObservedObject
将State
视为视图的单一真相来源,作为改变变量并使视图无效以反映该状态的手段。
Binding
则是视图和其基础模型之间的双向连接。一种改变不由视图管理的State
的手段(例如,一个切换按钮反映和控制布尔值,而控件本身对其存储或起源没有任何了解)
最后,您可以通过使用$
前缀运算符从任何State
中获取Binding
。
选择它们之间的简单指南:
想象一种情况,你有两个SwiftUI视图。在第一个视图中,你声明了一个count
属性,在第二个视图中,你创建了一个Tap Me
按钮。当按钮被点击时,第一个视图中的count
值应该被更新。为了实现这个逻辑,你需要在第一个视图中使用@State
属性包装器,在第二个视图中使用@Binding
属性包装器。
@State
允许局部地操作值类型
的小量数据。@State
直接创建和管理值,因此它是一个真实数据源
。@Binding
也引用值类型
的数据,但是由不同的视图拥有。@Binding
不是一个真实的数据源。为了将@State
属性传递给Binding<T>
,你需要使用$
运算符(即它看起来像$count
)。@Binding
创建了一个属性和另一个视图之间的双向连接。
以下是代码:
import SwiftUI
struct FirstView: View {
@State private var count: Int = 0
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
VStack {
SecondView(counter: $count).frame(width: 300, height: 100)
Text("Tapped \(count) times").foregroundColor(.white)
}
}
}
}
struct SecondView: View {
@Binding var counter: Int
var body: some View {
ZStack {
Color.yellow
Text("Tap Me").onTapGesture { counter += 1 }
}
}
}
# | 属性包装器 | 真相之源 | 用途 | 语义 |
---|---|---|---|---|
01 | @AppStorage | 是 | 从UserDefaults中读取/写入 | 值 |
02 | @Binding | 否 | 创建双向连接 | 值 |
03 | @Environment | 否 | 从系统中读取数据 | 值 |
04 | @EnvironmentObject | 否 | 从多个视图中读取共享对象 | 引用 |
05 | @FetchRequest | 是 | 用于CoreData的获取请求 | 值 |
06 | @FocusedBinding | 否 | 观察和解包来自焦点视图的状态绑定 | 值 |
07 | @FocusedValue | 否 | @FocusedBinding的简化版本 | 值 |
08 | @GestureState | 是 | 存储活动手势的值 | 值 |
09 | @Namespace | 是 | 是防止名称冲突的namespace.id的包装器 | 值 |
10 | @ObservedObject | 否 | 引用符合ObservableObject协议的外部类的实例 | 引用 |
11 | @Published | 是 | 附加到ObservableObject内的属性 | 值 |
12 | @ScaledMetric | 是 | 允许应用视图根据用户的动态类型设置进行缩放 | 值 |
13 | @SceneStorage | 是 | 恢复系统状态的轻量级数据 | 值 |
14 | @State | 是 | 在本地操作视图的数据 | 值 |
15 | @StateObject | 是 | 存储符合ObservableObject协议的新实例 | 引用 |
16 | @NSApplicationDelegateAdaptor | 是 | 创建一个AppKit应用程序委托 | 协议 |
17 | @UIApplicationDelegateAdaptor | 是 | 创建一个UIKit应用程序委托 | 协议 |
18 | @Observable | 宏 | ObservableObject + @Published + @ObservedObject模式的iOS 17替代品 | 引用 |
19 | @Query | 宏 | 读取由SwiftData管理的目标对象 | 引用 |
20 | @Model | 宏 | 使类能够从SwiftData加载和存储 | 引用 |
以下是使用四个常用属性包装器的示例。
State(状态)
@State
关键字允许我们请求 SwiftUI 监控属性的值。一旦值发生更改,视图将被无效并以高效的方式重新呈现。@propertyWrapper
,用于概述真相来源。Binding(绑定)
@Binding
和 $
前缀允许将 State
属性传递到嵌套的子级中。@Binding
是另一个明确依赖于 state 的 @propertyWrapper
。Binding
属性包装器,您可以定义对真实来源的显式依赖关系,而不拥有它,此外,您无需指定初始值,因为绑定可以从 state 派生。参考链接:Medium
我想提供一个真正简短的“真实用例”解释,这让我理清了它。 我没有定义State/Binding,我只是指出了它们之间的重大区别。
@State
保存值,是“事实来源”@Binding
传递值,像管道一样使用。关于@State的一个重要事项:更改会触发重绘。更改@State的值将导致整个视图“重新执行”。
这里是我为自己准备的笔记,
@State:
@Binding:
谢谢!
状态 简单的属性,比如字符串、整数和布尔值,属于单个视图 - 标记为私有。
绑定 复杂的属性,比如自定义类型,在许多视图中共享数据。对于引用类型是必需的。
EnvironmentObject 在其他地方创建的属性,比如共享数据,如果缺少会导致应用程序崩溃。
@State
或@Binding
:
@State
变量@State
-> @Binding
@State
@State
传递给子视图@Binding
以被动地跟踪@State
的变化@StateObject
<-> @EnvironmentObject
@StateObject
@StateObject
通过.environmentObject()
传递给最高祖先视图@EnvironmentObject
存储自己的引用使用@StateObject
和@EnvironmentObject
,可以直接与孙子共享数据,这些包装器相当于@State
和@Binding
,但允许跳过子级