SwiftUI:使用 @EnvironmentObject 从环境中读取自定义值

时间:2022-07-24
本文章向大家介绍SwiftUI:使用 @EnvironmentObject 从环境中读取自定义值,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

SwiftUI的环境使我们可以使用来自外部的值,这对于读取Core Data上下文或视图的展示模式等很有用。但是我们也可以将自定义对象发送到环境中,并在以后将它们读出来,这使我们可以在复杂的应用程序中更轻松地共享数据。

您已经了解了如何使用@State处理单个视图的局部状态,以及@ObservedObject如何使我们在视图之间传递一个对象,以便我们可以共享它。好吧,@ EnvironmentObject更进一步:我们可以将对象放置到环境中,以便任何子视图都可以自动访问它。

假设我们在一个应用程序中有多个视图,所有视图都排成一排:视图A显示视图B,视图B显示视图C,C显示D,D显示E。视图A和E都希望访问同一对象,但是要从A到达E,您需要经过B,C和D,而他们并不关心该对象。如果我们使用@ObservedObject,则需要将我们的对象从每个视图传递到下一个视图,直到它最终到达可以使用该视图的视图E,这很烦人,因为B,C和D不在乎它。使用@EnvironmentObject,视图A可以将对象放入环境中,视图E可以从环境中读取对象,而视图B,C和D不必知道发生了什么。

环境对象的一个​​复杂性是其子对象的构成,因为视图可以访问的环境对象取决于其父视图。例如,如果视图A可以访问环境对象,而视图B在视图A的内部——即视图B放在A的body属性中——那么视图B也可以访问该环境对象。这意味着,如果视图A是导航视图,则所有压入导航堆栈的视图都可以访问同一环境。但是,如果视图A以工作表(sheet)的形式显示视图B,则它们不会自动共享环境数据,因此我们需要手动发送。Apple已将此工作表情况描述为他们想要修复的错误,因此我希望在以后对SwiftUI的更新中会有所改变。

在向您展示一些代码之前,还有最后一件事:环境对象使用您已经学过的ObservableObject协议,SwiftUI将自动确保共享同一环境对象的所有视图在更改时都会更新。

好的,让我们看一些代码,这些代码展示了如何使用环境对象在两个视图之间共享数据。首先,这是我们可以使用的一些基本数据:

class User: ObservableObject {
    @Published var name = "Taylor Swift"
}

如您所见,使用ObservableObject@Published就像我们以前学到的那样——您积累的所有知识将继续得到回报。

接下来,我们可以定义两个SwiftUI视图以使用我们的新类。这些将使用@EnvironmentObject属性包装器来表示此数据的值来自环境,而不是在本地创建:

struct EditView: View {
    @EnvironmentObject var user: User

    var body: some View {
        TextField("Name", text: $user.name)
    }
}

struct DisplayView: View {
    @EnvironmentObject var user: User

    var body: some View {
        Text(user.name)
    }
}

@EnvironmentObject属性包装器将自动在环境中查找User实例,并将其找到的内容放入user属性中。如果在环境中找不到用户,您的代码就会

,因此请小心

现在,我们可以将这两个视图放在一个地方,并发送一个User实例供它们使用:

struct ContentView: View {
    let user = User()

    var body: some View {
        VStack {
            EditView().environmentObject(user)
            DisplayView().environmentObject(user)
        }
    }
}

这就是使我们的代码正常工作所要做的一切——您现在就可以运行该应用并更改文本字段,以查看其值显示在下面的文本视图中。当然,我们可以在单个视图中表示出来,但是通过这种方式,您可以确切地看到使用环境对象时通信的无缝性。

现在,这是最聪明的部分。尝试将ContentViewbody属性重写为:

VStack {
    EditView()
    DisplayView()
}
.environmentObject(user)

您会发现它的表现完全相同。现在,我们将用户置于ContentView环境中,但是由于EditViewDisplayView都是ContentView的子级,因此它们会自动继承其环境。

现在,您可能想知道SwiftUI如何在.environmentObject(user)@EnvironmentObject var user: User之间建立连接——如何知道将该对象放入正确的属性?

好吧,您已经了解到字典如何让我们使用一种类型作为键key,而另一种类型作为值。环境有效地使我们可以将数据类型本身用作键,并将类型的实例用作值。刚开始时,这有点令人费解,但可以这样想象:键是IntStringBool之类的,值是5,“ Hello”和 true,这意味着我们可以说“给我Int”,我们将得到5。

译自 Reading custom values from the environment with @EnvironmentObject