为何使用.sheet调用含EnvironmentObject的View崩溃,NavigationLink却正常?
问题
在SwiftUI开发中,使用.sheet展示依赖EnvironmentObject的Inputview时,应用崩溃并抛出错误:
Fatal error: No ObservableObject of type Blabla found. A View.environmentObject(_:) for Blabla may be missing as an ancestor of this view.
但使用NavigationLink结合NavigationStack打开该视图时,应用却能正常运行。多次尝试用.sheet结合EnvironmentObject编写小型应用,始终遇到该错误,改用NavigationLink后问题解决。相关代码如下:
ContentView 代码
struct ContentView: View { @State var showInputView = false @StateObject var myCars = Cars(autos: [Car( type: "Mercedes"), Car(type: "Porsche"), Car( type: "Audi")]) var body: some View { NavigationStack{ VStack { List(myCars.cars, id: \.id){car in Text(car.type) } NavigationLink { Inputview() } label: { Text("Show Input View") } Button("Button InputView"){ showInputView = true } .sheet(isPresented: $showInputView){ Inputview() } } } .environmentObject(myCars) .sheet(isPresented: $showInputView){ Inputview() } .padding() } }
Inputview 代码
struct Inputview: View { @EnvironmentObject var cars: Cars var body: some View { VStack{ List(cars.cars, id: \.id){auto in Text(auto.type)} } } }
原因分析
NavigationStack属于当前视图树的一部分,你把.environmentObject(myCars)添加到了NavigationStack的外部,所以NavigationLink跳转的Inputview会自动继承这个环境对象——因为它是NavigationStack的子视图,属于同一层级链。- 而
.sheet弹出的是模态视图,它的视图层级完全独立于当前的视图树,不会自动继承父视图的环境对象。你代码里的两个.sheet都直接实例化了Inputview(),没有给它注入对应的EnvironmentObject,所以运行时会找不到对象导致崩溃。
另外你代码里绑定了两个.sheet到同一个showInputView变量,点击按钮时会弹出两个模态框,这也是需要修复的冗余代码。
解决方案
当然可以用.sheet结合EnvironmentObject,只需要给模态视图明确注入环境对象即可,有两种常见方式:
方式一:直接给.sheet内的视图添加环境对象
修改.sheet的代码,给Inputview加上.environmentObject(myCars):
.sheet(isPresented: $showInputView){ Inputview() .environmentObject(myCars) }
这种方式最直观,明确给独立的模态视图注入所需的环境对象。
方式二:将环境对象注入到更高层级(可选)
如果你有多个模态视图都需要用到同一个环境对象,可以把.environmentObject(myCars)添加到整个ContentView的最外层,比如:
var body: some View { NavigationStack{ // ... 原有内容 } .padding() .environmentObject(myCars) // 移到最外层 }
不过因为模态视图的独立性,更推荐方式一,让依赖关系更清晰。
最后记得删除重复的.sheet绑定,只保留一个即可。
内容的提问来源于stack exchange,提问作者Lothar Requardt

