使用SwiftUI的环境传递标准类型(Int,String等)以查看层次结构

时间:2019-11-12 12:30:50

标签: swift swiftui

我知道我们可以使用ObservableObject传递自定义.environmentObject(_:)的类型,然后使用@EnvironmentObject特殊属性从子视图访问它。

但是我们希望在视图周围传递非定制的标准IntString属性吗?

我唯一看到的候选人是:

func environment<V>(_ keyPath: WritableKeyPath<EnvironmentValues, V>, _ value: V) -> some View

但是似乎它仅适用于固定的,非自定义的KeyPath,例如\.colorScheme

换句话说,我希望使用环境来传递@State

2 个答案:

答案 0 :(得分:3)

TL; DR

@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?

中找到这种方法的广泛使用