我知道我们可以使用ObservableObject
传递自定义.environmentObject(_:)
的类型,然后使用@EnvironmentObject
特殊属性从子视图访问它。
但是我们希望在视图周围传递非定制的标准Int
,String
属性吗?
我唯一看到的候选人是:
func environment<V>(_ keyPath: WritableKeyPath<EnvironmentValues, V>, _ value: V) -> some View
但是似乎它仅适用于固定的,非自定义的KeyPath
,例如\.colorScheme
。
换句话说,我希望使用环境来传递@State
。
答案 0 :(得分:3)
@State
设计用于单个视图(或通过Binding
传递到后代视图)。避免为每个视图创建ObservableObject
类只是一种方便。但是,如果要将数据传递到未紧密连接的多个视图,则应将数据包装在ObservableObject
类中,并使用.environmentObject()
要详细介绍krjw的评论以及为什么,您不能绕过@State
属性,我们来讨论一下@State
,@ObservedObject
,{{ 1}},@EnvironmentObject
和@Published
属性包装程序实际上正在执行。
因为您的视图是一个结构,所以它是一个值类型。当应用程序的状态发生变化,结果视图必须以不同的方式显示时,您当前的视图结构将被丢弃,并在其位置创建一个新的视图结构。但这意味着无法将数据存储在结构体中,因为每次视图更改时,数据都将被丢弃并替换为初始值。
输入@Binding
协议。创建符合此协议的对象并标记其属性ObservableObject
时,这是一个很好的语法,它可以说:“我有这个参考对象,我打算代表状态;请设置合并一些可以随时广播的组合发布商。”因为它是引用类型,所以它可以位于内存中某个位置,位于我们视图的外部,并且只要我们的视图引用了该视图并订阅了它的发布者,他们就可以知道它何时更改,以及相应地刷新。
但是请记住,我们说过我们的观点是结构。即使您为其中的一个属性分配了对@Published
的引用并订阅了其发布者,每当重新创建该结构时,我们都将丢失该信息。这就是ObservableObject
包装器的来源。实际上,这是在说:“将引用和对该对象的引用和订阅存储在我的结构 outside 中,并在重新创建视图时,使确定我知道我应该引用并收听此对象。”
@ObservedObject
做同样的事情,这是避免必须通过初始化程序传递@EnvironmentObject
的好语法。相反,它说:“相信我,您的一位祖先将拥有这种类型的物体。只需向他们询问。”
实际上,这足以完成我们在SwiftUI中需要做的所有事情。但是,如果我只需要存储和观看ObservedObject
的更改,并且只需要一个视图就该怎么办?为此目的,要使整个类变得繁重。这就是Bool
属性包装器的所在。它基本上只是将@State
/ ObservedObject
的功能与@Published
的功能组合在一起。它说:“将数据保留在我的结构体的外部中(以便在刷新时不会被丢弃),并在发生任何更改时通知我。”这就是为什么您可以修改@ObservedObject
属性的原因,即使修改结构的属性应重新创建该结构;数据不是真正在结构内 。
@State
只是使用@State
包装器来获得ObservableObject
类功能的一种浓缩,便捷的方法,该包装器仅在一个结构中使用。
为完整起见,让我们谈谈@ObservedObject
。有时,我们视图的直接后代可能需要修改其祖先的@Binding
变量(例如,@State
需要修改其父视图持有的TextField
值)。 String
属性包装器的意思是:“您不需要存储此数据(因为它已经被另一个视图/对象存储了);您只需要查看它的更改并可以写回更改。 “
注意:是的,实际上,@Binding
并不是在建立符合@State
和另一个ObservableObject
包装器的类;它的效率要高得多。但是,从使用的角度来看,功能上就是这样。
答案 1 :(得分:0)
在需要时,我通过参数或构造函数将@State传递为Binding。您可以在我的代码How do I create a multiline TextField in SwiftUI?
中找到这种方法的广泛使用