You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的目标视图被重置,触发导航栈回到初始页。

示例项目修复

  1. 移除NavigationLink目标中的.environmentObject(fooConfig),EnvironmentObject会自动在导航栈中传递。
  2. 不要用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 21:33:22