SwiftUI
的@EnvironmentObjec
t是一个强大的工具,它允许你在多个视图之间共享数据(使用一个可观察对象)。当你有一个复杂的视图层次结构,并且需要在没有直接连接的视图之间共享相同的可观察对象时,它特别有用。
我们之前传递数据主要是通过init
方法一层一层的传递,一层两层还行,要是再多了,代码很难维护,逻辑也比较复杂,这可能需要大量的工作,而且有的中间层根本用不到这个传递的数据。
使用EnvironmentObject
允许将一个对象(一个可观察对象的实例)附加到父视图。父视图的任何后代(比如push出来的所有视图等)现在都可以访问该对象。
要使用环境对象,首先需要创建一个符合ObservableObject
协议的ObservableObject
类,这个类将保存我们希望在视图之间共享的数据。
class GameSettings: ObservableObject {
@Published var score = 0
}
在主界面创建ObservableObject
实例,并且设置要共享的数据。
struct EnvironmentObjectDemo: View {
@StateObject var settings = GameSettings()
var body: some View {
NavigationView {
VStack(spacing: 30) {
Text("\(settings.score)")
.font(.title)
Button("Increase Score") {
settings.score += 1
}
NavigationLink {
DetailView()
} label: {
Text("Show Detail View")
}
}
.frame(height: 200)
}
.environmentObject(settings)
}
}
该界面首先要创建一个ObservableObject
实例:
@StateObject var settings = GameSettings()
然后在视图上添加.environmentObject
修饰符,并传入要共享的数据:
.environmentObject(settings)
界面中点击“Increase Score”按钮修改score
,点击“Show Detail View”按钮跳转到下一个DetailView
界面。
struct DetailView: View {
var body: some View {
NavigationLink {
ScoreView()
} label: {
Text("Show Score View")
.font(.title)
}
}
}
在DetailView
界面,没有用到score
,而是点击"Show Score View"按钮继续跳转到下一个ScoreView
界面,并在这个界面显示score
。
struct ScoreView: View {
@EnvironmentObject var settings: GameSettings
var body: some View {
Text("Score: \(settings.score)")
}
}
在ScoreView
界面中,通过@EnvironmentObject
包装器访问共享数据:
@EnvironmentObject var settings: GameSettings
最终效果如下:
几点总结:
- 就像
@StateObject
和@ObservedObject
一样,与@EnvironmentObject
一起使用的所有类都必须遵守ObservableObject
协议。 - 我们将
settings
放入导航堆栈的环境中,这意味着导航堆栈中的所有视图都可以读取该对象,以及导航堆栈显示的任何视图。 - 当使用
@EnvironmentObject
属性包装器时,只要声明期望接收的对象的类型,而不是创建它,我们希望从环境中接收它。 - 因为显示的
ScoreView
在导航堆栈中,它访问相同的环境,这意味着它可以读取我们创建的GameSettings
实例对象。 - 我们不需要显式地将环境中的
GameSettings
实例与ScoreView
中的settings
属性关联起来——SwiftUI
会自动计算出它在环境中有一个GameSettings
实例,并使用这个实例。即使创建时的变量名和使用的时候定义的变量名不同也没关系。
如果需要向环境中添加多个对象,则应该添加多个.environmentObject
修饰符,一个一个的添加即可。
struct EnvironmentObjectDemo: View {
@StateObject var gameSettings = GameSettings()
@StateObject var videoSettings = VideoSettings()
var body: some View {
NavigationView {
......
}
.environmentObject(gameSettings)
.environmentObject(videoSettings)
}
}
另外如果创建了两个同类型的ObservableObject
对象,并且依次添加到环境中去,那么最早添加的那个起作用,后添加的无效。
上面代码创建了2个GameSettings
的实例对象,并通过.environmentObject
修饰符向下传递。代码中改变了gameSettings2
的score
值,但是最后到ScoreView
界面还是显示的gameSettings1
传递的score值。
最后,希望能够帮助到有需要的朋友,如果您觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。