SwiftUI - @State与@Binding的区别。

56

我正在学习使用Swift和SwiftUI进行iOS编程。 我所知甚少,对于@State@Binding之间的区别非常困惑。

如果我理解正确,@Binding本质上就是@State,但它不会更新视图。 如果确实如此,那么既然可以使用@State来完成相同的事情,为什么还需要@Binding呢?

9个回答

57

@State@Binding都是属性包装器。

@State

  • 它用于每次更新变量的值。
  • 我们也可以说它是双向绑定。
  • 如果我们更改属性状态,那么SwiftUI会自动重新加载视图的内容。
  • 它适用于简单的属性,如字符串、整数和布尔值。

@Binding

  • 使用它,您可以访问另一个视图的状态属性。
  • 它将为您提供变量的读写访问权限。

如果我有一个自定义类型,它是我的视图中的一个属性,可以由用户更新,那么在该属性上使用@State是否是不良实践? - Tank12

54
  • @EnvironmentObject is a property wrapper type that's used to access an object from the environment.
  • It enables sharing data between all views in the app, and changes made to the shared object will cause any subscribed views to update automatically.
  • The object should conform to the ObservableObject protocol and be added to the environment using the EnvironmentObject modifier.
  • @ObservedObject

    • @ObservedObject is a property wrapper type that's used to watch an object for changes.
    • It's similar to @State, but instead of being connected only to one view, it can be used across multiple views.
    • Any changes made to the observed object will cause any subscribed views to update automatically.
  • 它是环境特性的一部分。您可以使用所有所需的服务类填充您的环境,然后从该环境内的任何视图中访问它们。
  • @EnvironmentObject 可以在环境内的每个视图中访问。
  • @EnvironmentObject属性在其他地方创建,例如共享数据,如果缺少则应用程序会崩溃。
  • 环境是SwiftUI依赖注入的正确方式。

  • 43

    State视为视图的单一真相来源,作为改变变量并使视图无效以反映该状态的手段。

    Binding则是视图和其基础模型之间的双向连接。一种改变不由视图管理的State的手段(例如,一个切换按钮反映和控制布尔值,而控件本身对其存储或起源没有任何了解)

    最后,您可以通过使用$前缀运算符从任何State中获取Binding

    选择它们之间的简单指南:

    • 我需要修改一个对我私有的值吗?--> State
    • 我需要修改某个其他视图的State吗?--> Binding

    29

    关于@State和@Binding

    想象一种情况,你有两个SwiftUI视图。在第一个视图中,你声明了一个count属性,在第二个视图中,你创建了一个Tap Me按钮。当按钮被点击时,第一个视图中的count值应该被更新。为了实现这个逻辑,你需要在第一个视图中使用@State属性包装器,在第二个视图中使用@Binding属性包装器。

    @State允许局部地操作值类型的小量数据。@State直接创建和管理值,因此它是一个真实数据源@Binding也引用值类型的数据,但是由不同的视图拥有。@Binding不是一个真实的数据源。为了将@State属性传递给Binding<T>,你需要使用$运算符(即它看起来像$count)。@Binding创建了一个属性和另一个视图之间的双向连接。

    以下是代码:

    enter image description here

    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 }
            }
        }
    }
    

    除了上述的属性包装器之外,还可以通过所谓的“三位一体”工具包(`@StateObject`、`@Published`和`@ObservedObject`)来实现类似的结果。

    属性包装器

    下面的数据透视表表示了20个常用的SwiftUI属性包装器的三个主要特征(真实性来源、用途和语义)。
    # 属性包装器 真相之源 用途 语义
    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加载和存储 引用

    如何...

    以下是使用四个常用属性包装器的示例。


    13

    State(状态)

    • @State 关键字允许我们请求 SwiftUI 监控属性的值。一旦值发生更改,视图将被无效并以高效的方式重新呈现。
    • A persistent value of a given type, through which a view reads and monitors the value.(给定类型的持久值,通过它视图读取和监视值。)
    • 这只是另一个 @propertyWrapper,用于概述真相来源。
    • 当您使用 state 时,框架为变量分配持久性存储并将其跟踪为依赖项......您始终必须指定初始常量值。

    Binding(绑定)

    • @Binding$ 前缀允许将 State 属性传递到嵌套的子级中。
    • 用于管理值的管理器,提供了一种改变值的方法。
    • @Binding 是另一个明确依赖于 state 的 @propertyWrapper
    • 通过使用 Binding 属性包装器,您可以定义对真实来源的显式依赖关系,而不拥有它,此外,您无需指定初始值,因为绑定可以从 state 派生。

    参考链接:Medium


    9

    我想提供一个真正简短的“真实用例”解释,这让我理清了它。 我没有定义State/Binding,我只是指出了它们之间的重大区别。

    @State 保存值,是“事实来源”

    @Binding 传递值,像管道一样使用。

    关于@State的一个重要事项:更改会触发重绘。更改@State的值将导致整个视图“重新执行”。


    4

    这里是我为自己准备的笔记,

    @State:

    • 我们需要将其放在视图结构体内部使用
    • 建议将其设置为私有(private)
    • 应该提供默认值
    • 可以用作绑定(binding)
    • 这旨在存储简单的类型,如StringIntBool等...

    @Binding:

    • 这用于在视图之间共享公共数据
    • 最好的例子是,在View 1中呈现一个sheet并从View 2启动dismiss操作
    • 不需要默认值因为将从另一个视图设置它

    谢谢!


    2

    状态 简单的属性,比如字符串、整数和布尔值,属于单个视图 - 标记为私有。

    绑定 复杂的属性,比如自定义类型,在许多视图中共享数据。对于引用类型是必需的。

    EnvironmentObject 在其他地方创建的属性,比如共享数据,如果缺少会导致应用程序崩溃。


    0
    使用3个简单的启发法来决定何时使用@State@Binding
    1. 当一个单个视图改变状态时重新绘制:
    • 视图管理自己的@State变量
    1. 当一个子视图父视图改变状态时重新绘制:

    @State -> @Binding

    • 父视图管理@State
    • 父视图通过构造函数将@State传递给子视图
    • 子视图保持@Binding以被动地跟踪@State的变化
    1. 当层次结构中的更改发生时,一个或多个孙子视图重新绘制:

    @StateObject <-> @EnvironmentObject

    • 顶层视图管理@StateObject
    • @StateObject通过.environmentObject()传递给最高祖先视图
    • 所有子/孙子视图通过@EnvironmentObject存储自己的引用

    使用@StateObject@EnvironmentObject,可以直接与孙子共享数据,这些包装器相当于@State@Binding,但允许跳过子级


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接