如何为TabView的每个标签页实现独立NavigationStack导航栈?
问题根源
你遇到的核心问题是Router类使用了单例模式,导致所有标签页的RouterView共享同一个Router实例,进而共用导航历史(history数组)。
具体来说:
Router里的static let router: Router = Router()是全局唯一的单例,所有RouterView的@StateObject var router = Router.router都指向同一个实例。- 全局的
Router.navigate(_:)方法也直接操作这个单例的history,所以任何标签页的导航操作都会修改同一个栈,切换标签页时自然显示相同的子视图。
解决方案
要实现每个标签页独立导航栈,需要让每个RouterView持有独立的Router实例,并通过环境传递给子视图,避免单例共享。
1. 修改Router类,移除单例
删掉全局单例,将导航方法改为实例方法:
class Router: ObservableObject { @Published public var history: [Route] = [] // 移除单例,让每个RouterView创建自己的实例 init() {} // 改为实例方法,操作当前实例的history func navigate(_ route: Route) { history.append(route) } static func handle(_ route: Route) -> some View { switch route { case .something: return SomeView() } } } enum Route: Hashable { case something }
2. 修改RouterView,创建独立Router实例
让每个RouterView初始化自己的Router,并将其注入环境,方便子视图访问:
struct RouterView <Content: View>: View { // 每个RouterView拥有独立的Router实例 @StateObject private var router = Router() var content: () -> Content init(@ViewBuilder content: @escaping () -> Content) { self.content = content } var body: some View { NavigationStack(path: $router.history) { content() // 将当前Router注入环境,子视图可通过@EnvironmentObject获取 .environmentObject(router) .navigationDestination(for: Route.self) { route in Router.handle(route) } } } }
3. 子视图中使用当前标签页的Router
在需要导航的子视图(如HomeView)中,通过@EnvironmentObject获取当前标签页的Router实例,调用实例方法导航:
struct HomeView: View { // 获取当前标签页的Router实例 @EnvironmentObject private var router: Router var body: some View { Button("跳转到SomeView") { // 操作当前实例的history,仅影响当前标签页的导航栈 router.navigate(.something) } } }
验证效果
修改后,每个标签页的RouterView会创建独立的Router实例,各自维护自己的history数组。在一个标签页导航到子视图后,切换其他标签页仍会显示其初始视图,不会互相干扰。
内容的提问来源于stack exchange,提问作者goddamnyouryan
相关产品推荐
相关产品推荐

