使用NavigationStack推送视图时的@Published相关异常问题
问题本质是@Published var openDrinks的状态更新逻辑和NavigationStack的导航机制冲突:如果用Bool类型的Published变量触发跳转,当变量重复发送true信号(比如意外重复赋值、状态未及时重置),会导致NavigationStack重复推送页面;同时若状态管理不当,可能干扰导航栈的层级结构,误删OrderFood视图。
以下是具体解决方法:
方案1:改用导航路径数组+枚举管理(最可靠)
放弃用Bool变量触发跳转,改用可哈希的枚举定义所有导航目标,结合NavigationStack的path绑定来精确控制栈结构:
步骤1:在ViewModel中定义导航枚举和路径数组
class OrderViewModel: ObservableObject { // 枚举所有需要跳转的页面 enum NavDestination: Hashable { case drinkView // 其他页面可在此扩展 } @Published var navPath = [NavDestination]() // 封装跳转方法,避免重复添加 func pushDrinkView() { // 先检查路径中是否已存在目标,防止重复推送 if !navPath.contains(.drinkView) { navPath.append(.drinkView) } } }
步骤2:在View中绑定路径并处理导航目标
NavigationStack(path: $viewModel.navPath) { OrderFoodView() // 给OrderFoodView添加触发跳转的按钮,调用pushDrinkView() .toolbar { Button("打开饮品页") { viewModel.pushDrinkView() } } // 根据枚举匹配对应的目标视图 .navigationDestination(for: OrderViewModel.NavDestination.self) { dest in switch dest { case .drinkView: DrinkView() // 可选:返回时可移除路径中的对应元素(也可依赖系统返回按钮自动处理) .toolbar { Button("返回") { viewModel.navPath.removeLast() } } } } }
这种方式完全由数组navPath控制导航栈的层级,不会出现重复推送或误删页面的问题,还能灵活管理多页面跳转。
方案2:修复Bool类型Published变量的触发逻辑(适合简单场景)
如果坚持用@Published var openDrinks: Bool,需确保状态只触发一次跳转,并及时重置:
步骤1:ViewModel中封装跳转触发方法
class OrderViewModel: ObservableObject { @Published var openDrinks = false func openDrinkView() { // 先重置状态,避免重复触发 openDrinks = false // 异步设置为true,确保状态更新是单次有效 DispatchQueue.main.async { self.openDrinks = true } } }
步骤2:在View中处理跳转并重置状态
NavigationStack { OrderFoodView() .toolbar { Button("打开饮品页") { viewModel.openDrinkView() } } .navigationDestination(isPresented: $viewModel.openDrinks) { DrinkView() // 页面消失时重置状态,防止下次触发异常 .onDisappear { viewModel.openDrinks = false } } }
额外注意事项
- 不要同时混用
navigationDestination(isPresented:)和path绑定两种导航方式,会导致栈状态混乱; - 检查ViewModel中是否有多处修改
openDrinks的逻辑,避免重复赋值; - 若用Combine监听
openDrinks的变化,确保只绑定一次跳转逻辑,不要重复订阅。
内容的提问来源于stack exchange,提问作者Amr AbdelWahab
相关产品推荐
相关产品推荐

