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)
}
}
}
这就是使我们的代码正常工作所要做的一切——您现在就可以运行该应用并更改文本字段,以查看其值显示在下面的文本视图中。当然,我们可以在单个视图中表示出来,但是通过这种方式,您可以确切地看到使用环境对象时通信的无缝性。
现在,这是最聪明的部分。尝试将ContentView
的body
属性重写为:
VStack {
EditView()
DisplayView()
}
.environmentObject(user)
您会发现它的表现完全相同。现在,我们将用户置于ContentView
环境中,但是由于EditView
和DisplayView
都是ContentView
的子级,因此它们会自动继承其环境。
现在,您可能想知道SwiftUI如何在.environmentObject(user)
和@EnvironmentObject var user: User
之间建立连接——如何知道将该对象放入正确的属性?
好吧,您已经了解到字典如何让我们使用一种类型作为键key
,而另一种类型作为值。环境有效地使我们可以将数据类型本身用作键,并将类型的实例用作值。刚开始时,这有点令人费解,但可以这样想象:键是Int
,String
和Bool
之类的,值是5,“ Hello”和 true,这意味着我们可以说“给我Int
”,我们将得到5。
译自 Reading custom values from the environment with @EnvironmentObject