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

使用Point-Free的swiftui-navigation时深层路由初始化异常问题

SwiftUI-Navigation 初始化深层路由自动回退问题修复

问题现象

使用Point-Free的swiftui-navigation库时:

  • 初始化到第一屏(ContentView(viewModel: .init(route: nil)))或第二屏(ContentView(viewModel: .init(route: .second(route: nil))))时,导航功能正常。
  • 初始化到第三屏(ContentView(viewModel: .init(route: .second(route: .third))))时,应用先显示第三屏,随后自动退回第二屏。

问题原因

问题出在NavigationLink的onNavigate回调实现上。当初始化深层路由时,SwiftUI在同步导航状态的过程中会触发onNavigate,而当前代码中直接将第二屏的子路由重置为nil:

// ContentView中错误的onNavigate实现
onNavigate: { isActive in
    viewModel.route = isActive ? .second(route: nil) : nil
}

这会覆盖掉初始化时传入的.third子路由,导致第三屏被自动弹出。

同理,SecondView中的onNavigate也存在类似逻辑,不过主要触发点在ContentView的回调。

解决方案

修改onNavigate的逻辑,仅在用户主动关闭导航(比如点击返回按钮)时更新路由,初始化时保留原有的深层路由结构。

修改后的完整代码

import SwiftUINavigation
import SwiftUI

class ContentViewModel: ObservableObject {
    @Published var route: Route? = nil

    init(route: Route? = nil) {
        self.route = route
    }

    enum Route: Equatable {
        case second(route: SecondViewModel.Route?)
    }
}

struct ContentView: View {
    @StateObject var viewModel: ContentViewModel

    var body: some View {
        NavigationView {
            NavigationLink(
                unwrapping: $viewModel.route,
                case: /ContentViewModel.Route.second,
                onNavigate: { isActive in
                    // 仅在关闭导航时清空路由,打开时保留原有子路由
                    if !isActive {
                        viewModel.route = nil
                    }
                }
            ) { $route in
                // 绑定子路由,让SecondViewModel能同步更新
                SecondView(viewModel: .init(route: route))
            } label: {
                Text("One")
            }
        }
    }
}

class SecondViewModel: ObservableObject {
    @Published var route: Route? = nil

    init(route: Route? = nil) {
        self.route = route
    }

    enum Route: Equatable {
        case third
    }
}

struct SecondView: View {
    @StateObject var viewModel: SecondViewModel

    var body: some View {
        NavigationLink(
            unwrapping: $viewModel.route,
            case: /SecondViewModel.Route.third,
            onNavigate: { isActive in
                // 同样仅在关闭时清空路由
                if !isActive {
                    viewModel.route = nil
                }
            }
        ) { $route in
            Text("Three")
        } label: {
            Text("Two")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(viewModel: .init(route: .second(route: .third)))
    }
}

关键修改点

  1. ContentView的onNavigate:移除了打开导航时重置子路由的逻辑,仅在isActive为false(用户关闭导航)时清空viewModel.route。这样初始化时传入的.second(route: .third)会被完整保留,不会被覆盖。
  2. SecondView的onNavigate:同理,仅在关闭第三屏时清空子路由,避免初始化时的误触发。
  3. 子路由绑定:通过$route将ContentView的子路由绑定到SecondViewModel,保证深层路由的状态能正确同步。

这样修改后,初始化到第三屏时,路由结构会被完整保留,不会出现自动回退的问题。

内容的提问来源于stack exchange,提问作者Sergio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 15:56:58