SwiftUI NavigationStack无限循环及页面跳转异常问题咨询
问题场景
使用NavigationStack实现页面跳转时,点击按钮设置showRegistrationView = true后,ForgotPasswordView或RegistrationView会被无限调用;手动在目标视图的onAppear中重置状态后,虽能阻止无限加载,但关闭目标视图时,LoginView会短暂显示后跳转到空白页面,点击空白页的返回按钮才能回到初始视图。
相关代码
根视图代码:
@State private var showLoginView = false var body: some View { NavigationStack { content() .navigationDestination(isPresented: $showLoginView) { LoginView(viewModel: LoginViewModel(pageId: "login_view")) }.navigationDestination(isPresented: $showRegistrationView) { RegistrationView(viewModel: RegistrationViewModel(pageId: "registration_view")) }.navigationDestination(isPresented: $continueAsGuest, destination: { TabbedView(viewModel: TabbedViewModel()) }) } }
LoginView代码:
var body: some View { content() .navigationDestination(isPresented: $showForgotPasswordView, destination: { ForgotPasswordView(viewModel: ForgotPasswordViewModel(pageId: "forgot_password_view")) }) .navigationDestination(isPresented: $showRegistrationView, destination: { RegistrationView(viewModel: RegistrationViewModel(pageId: "registration_view")) }) }
临时修复代码:
ForgotPasswordView(viewModel: ForgotPasswordViewModel(pageId: "forgot_password_view")).onAppear { self.showForgotPasswordView = false }
问题原因
- 重复注册跳转目标:根视图和LoginView中都注册了
RegistrationView(以及ForgotPasswordView)的navigationDestination。当showRegistrationView变为true时,两个注册的目标都会被触发,导致NavigationStack不断往栈里推入新视图,形成无限循环。 - 手动重置状态的副作用:在目标视图的
onAppear中立刻把绑定的布尔值设为false,会破坏NavigationStack的状态一致性——视图已经被推入栈,但触发跳转的状态被强行重置,关闭目标视图时,NavigationStack找不到正确的上一级页面,就会显示空白视图。
解决方案
1. 统一管理跳转目标
所有页面的navigationDestination都只在根NavigationStack中注册,子视图仅负责修改绑定的状态变量,不要重复注册目标,避免重复触发跳转。
2. 不要手动重置跳转状态
NavigationStack会自动在视图被弹出时,将绑定的布尔值设为false,无需手动在onAppear中修改。
修改后代码示例
根视图代码
@State private var showLoginView = false @State private var showRegistrationView = false @State private var showForgotPasswordView = false @State private var continueAsGuest = false var body: some View { NavigationStack { content() // 所有跳转目标统一在根层级注册 .navigationDestination(isPresented: $showLoginView) { LoginView( viewModel: LoginViewModel(pageId: "login_view"), showForgotPasswordView: $showForgotPasswordView, showRegistrationView: $showRegistrationView ) } .navigationDestination(isPresented: $showRegistrationView) { RegistrationView(viewModel: RegistrationViewModel(pageId: "registration_view")) } .navigationDestination(isPresented: $showForgotPasswordView) { ForgotPasswordView(viewModel: ForgotPasswordViewModel(pageId: "forgot_password_view")) } .navigationDestination(isPresented: $continueAsGuest) { TabbedView(viewModel: TabbedViewModel()) } } }
LoginView代码
// 通过Binding接收根视图的状态变量 @Binding var showForgotPasswordView: Bool @Binding var showRegistrationView: Bool let viewModel: LoginViewModel var body: some View { content() // 按钮直接修改绑定值即可,无需再注册navigationDestination .toolbar { Button("忘记密码") { showForgotPasswordView = true } Button("注册新账号") { showRegistrationView = true } } }
内容的提问来源于stack exchange,提问作者Easy Coder
相关产品推荐
相关产品推荐

