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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 22:17:51