Coordinator模式下NavigationPath导航失效问题求助
问题描述
我正在尝试实现Coordinator模式,每个页面都配有独立的Router、Screen和ViewModel。但点击登录页面按钮时,导航路径没有按预期工作:路径计数显示为2,符合预期,但Login视图就是不显示。
相关代码
初始化导航路径的主App路由
class AppFlowState: ObservableObject { @Published var path = NavigationPath() } class AppRouter: AppFlowState{ @ObservedObject var state = AppFlowState() @ViewBuilder func route(to destination: AppRoutes) -> some View { switch destination { case .home: self.homeScreen() } } func homeRoute() { self.path.append(AppRoutes.home) print(path.count) } private func homeScreen() -> some View { let router = HomeRouter(path: path) let screen = HomeScreen(router: router) return screen } }
负责视图路由的App页面
struct AppScreen: View { @ObservedObject var router = AppRouter() @ViewBuilder private func content() -> some View { VStack { Button { self.router.homeRoute() } label: { Text("Home Screen") }.buttonStyle(.borderedProminent) } .padding() } var body: some View { NavigationStack(path: self.$router.path) { content() .navigationDestination(for: AppRoutes.self) { screen in router.route(to: screen) } } } }
class HomeRouter: ObservableObject { @Published var path: NavigationPath init(path: NavigationPath) { self.path = path } @ViewBuilder func router(to destination: HomeRoutes) -> some View { switch destination { case .login: self.loginScreen() } } func loginRoute() { path.append(HomeRoutes.login) } private func loginScreen() -> some View { LoginScreen() } }
struct HomeScreen: View { @State var router: HomeRouter @ViewBuilder private func content() -> some View { VStack { Button { self.router.loginRoute() } label: { Text("Login Screen") }.buttonStyle(.borderedProminent) } } var body: some View { content() .navigationDestination(for: HomeRoutes.self) { screen in router.router(to: screen) } } }
问题解决方案
你的问题核心出在导航状态管理混乱、路由类型未在主导航栈注册、引用类型属性包装器使用错误三个点,以下是具体修正步骤:
1. 修复AppRouter的状态冗余问题
AppRouter继承AppFlowState后又声明内部state属性,导致路径状态重复且冲突,简化后统一管理状态:
class AppFlowState: ObservableObject { @Published var path = NavigationPath() } class AppRouter: ObservableObject { @ObservedObject private var state = AppFlowState() // 对外暴露可绑定的路径 var path: Binding<NavigationPath> { $state.path } @ViewBuilder func route(to destination: AppRoutes) -> some View { switch destination { case .home: homeScreen() } } func homeRoute() { state.path.append(AppRoutes.home) print(state.path.count) } private func homeScreen() -> some View { let router = HomeRouter(path: state.$path) HomeScreen(router: router) } }
2. 在主NavigationStack中注册所有路由类型
主导航栈需要识别所有可能的路由类型,因此要在AppScreen的NavigationStack中同时注册AppRoutes和HomeRoutes:
struct AppScreen: View { @ObservedObject var router = AppRouter() @ViewBuilder private func content() -> some View { VStack { Button { router.homeRoute() } label: { Text("Home Screen") }.buttonStyle(.borderedProminent) }.padding() } var body: some View { NavigationStack(path: router.path) { content() // 处理AppRoutes类型路由 .navigationDestination(for: AppRoutes.self) { screen in router.route(to: screen) } // 处理HomeRoutes类型路由 .navigationDestination(for: HomeRoutes.self) { screen in HomeRouter(path: router.path).router(to: screen) } } } }
3. 修正HomeScreen的属性包装器
HomeRouter是引用类型,不能使用适用于值类型的@State,改用@ObservedObject:
struct HomeScreen: View { @ObservedObject var router: HomeRouter @ViewBuilder private func content() -> some View { VStack { Button { router.loginRoute() } label: { Text("Login Screen") }.buttonStyle(.borderedProminent) } } var body: some View { content() } }
4. 优化HomeRouter的路径传递(可选)
由于NavigationPath是值类型,直接传递会产生副本,改为传递绑定可确保子路由直接修改主路径:
class HomeRouter: ObservableObject { @Binding var path: NavigationPath init(path: Binding<NavigationPath>) { self._path = path } @ViewBuilder func router(to destination: HomeRoutes) -> some View { switch destination { case .login: LoginScreen() } } func loginRoute() { path.append(HomeRoutes.login) } }
完成以上修改后,点击登录按钮时,主导航栈会正确识别HomeRoutes.login类型,并展示对应的Login视图。
内容的提问来源于stack exchange,提问作者mickeysox
相关产品推荐
相关产品推荐

