SwiftUI中@EnvironmentObject引发视图与导航异常问题求助
问题与修复方案

主项目问题
点击上下文菜单中的Move按钮,修改EnvironmentObject的isCopied和setOriginPath变量后,List视图被清空,无内容显示。不使用EnvironmentObject时无此问题。
主项目相关代码
上下文菜单代码
.contextMenu { Button { safeFileVM.hideSelectedFile(fileName: currentFile.fileName) safeFileVM.takeArrayOfItems() } label: { HStack { Text(!currentFile.isLock ? "Hide" : "Show") Image(systemName: currentFile.isLock ? "eye" : "eye.slash") } } Button { safeFileClipboard.setOriginPath = URL(fileURLWithPath: currentFile.localPath) safeFileClipboard.isCopied = true } label: { HStack { Text("Move") Image(systemName: "arrow.up.doc") } } }
视图代码
struct DetailObjectView: View { @ObservedObject var safeFileVM: SafeFileViewModel = SafeFileViewModel() @EnvironmentObject var safeFileClipboard: SafeFileClipBoard var currentFile: MyFile var currentLocation = "" var body: some View { VStack { ..... } .contextMenu { Button { safeFileVM.hideSelectedFile(fileName: currentFile.fileName) safeFileVM.takeArrayOfItems() } label: { HStack { Text(!currentFile.isLock ? "Hide" : "Show") Image(systemName: currentFile.isLock ? "eye" : "eye.slash") } } Button { safeFileClipboard.setOriginPath = URL(fileURLWithPath: currentFile.localPath) safeFileClipboard.isCopied = true } label: { HStack { Text("Move") Image(systemName: "arrow.up.doc") } } } } }
示例项目问题
修改EnvironmentObject的值时,导航会回到初始页面,请问原因及修复方案?
示例项目代码
主入口
@main struct EnvironmentTestApp: App { @StateObject var fooConfig: FooConfig = FooConfig() var body: some Scene { WindowGroup { NavigationView { ContentView() .environmentObject(fooConfig) } } } }
ContentView
struct ContentView: View { @EnvironmentObject var fooConfig: FooConfig private let numbers: [Number] = [.init(item: "1"), .init(item: "2"), .init(item: "3")] var body: some View { List { ForEach(numbers, id: \.id) { item in DetailView(itemNumber: item.item) } } } } struct Number: Identifiable { var id = UUID() var item: String }
DetailView
struct DetailView: View { @EnvironmentObject var fooConfig: FooConfig var itemNumber: String = "" var body: some View { NavigationLink(destination: ContentView().environmentObject(fooConfig)) { Text("\(itemNumber) - \(fooConfig.fooBool == true ? "On" : "Off")") .environmentObject(fooConfig) .contextMenu { Button { fooConfig.fooBool.toggle() } label: { HStack { Text(fooConfig.fooBool != true ? "On" : "Off") } } } } } }
ObservableObject代码
class FooConfig: ObservableObject { @Published var fooBool: Bool = false }
问题原因与修复方案
主项目List清空原因
DetailObjectView中的safeFileVM是直接在视图内初始化的@ObservedObject,当EnvironmentObject变化触发视图重新渲染时,safeFileVM会被重新创建,丢失之前的数据源状态,导致List无内容。
主项目修复
- 方案1:将
@ObservedObject改为@StateObject,确保ViewModel在视图生命周期内只初始化一次:
struct DetailObjectView: View { @StateObject var safeFileVM = SafeFileViewModel() // 替换原有的ObservedObject初始化 // ... 其余代码不变 }
- 方案2:如果
safeFileVM是父视图共享的实例,移除当前视图内的初始化,由父视图传入:
struct DetailObjectView: View { @ObservedObject var safeFileVM: SafeFileViewModel // 不在这里初始化 // ... 其余代码不变 } // 父视图调用时传入 DetailObjectView(safeFileVM: parentSafeFileVM, currentFile: someFile)
示例项目导航回退原因
DetailView的NavigationLink目标是重新创建ContentView实例并手动传入EnvironmentObject,当FooConfig的fooBool变化时,DetailView重新渲染,导致NavigationLink的目标视图被重置,触发导航栈回到初始页。
示例项目修复
- 移除
NavigationLink目标中的.environmentObject(fooConfig),EnvironmentObject会自动在导航栈中传递。 - 不要用
NavigationLink包裹文本跳转到ContentView(不符合常规导航逻辑),如果不需要跳转,直接使用Text即可:
struct DetailView: View { @EnvironmentObject var fooConfig: FooConfig var itemNumber: String = "" var body: some View { Text("\(itemNumber) - \(fooConfig.fooBool ? "On" : "Off")") .contextMenu { Button { fooConfig.fooBool.toggle() } label: { Text(fooConfig.fooBool ? "Off" : "On") } } } }
内容的提问来源于stack exchange,提问作者Ufuk Köşker
相关产品推荐
相关产品推荐

