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

SwiftUI中如何检测视图真正被 Dismiss 的时机?

检测SwiftUI视图真正被Dismiss的时机

针对你在NavigationStack+NavigationSplitView场景下的需求,这里提供几个可靠的实现方式:

方案1:绑定NavigationPath+自定义路由状态监控

虽然NavigationPath内部值私有,但你可以维护一个和NavigationPath同步的自定义路由数组,通过对比数组变化判断视图是否被真正弹出。

示例代码:

class Router: ObservableObject {
    @Published var path = NavigationPath()
    // 自定义路由状态数组,存储每个视图的标识(比如枚举值)
    @Published var routeStack: [Route] = []
    
    func push(_ route: Route) {
        path.append(route)
        routeStack.append(route)
    }
    
    func pop() {
        path.removeLast()
        routeStack.removeLast()
    }
    
    func popToRoot() {
        path.removeLast(path.count)
        routeStack.removeAll()
    }
}

// 定义路由枚举,标识不同视图
enum Route: Hashable {
    case menuItem1
    case menuItem2
    case detailView(id: Int)
}

// 使用时
struct ContentView: View {
    @StateObject private var router = Router()
    
    var body: some View {
        NavigationSplitView {
            List {
                Button("跳转菜单1") {
                    router.push(.menuItem1)
                }
                Button("跳转菜单2") {
                    router.push(.menuItem2)
                }
            }
        } detail: {
            NavigationStack(path: $router.path) {
                MapView() // 你的地图视图
                    .navigationDestination(for: Route.self) { route in
                        switch route {
                        case .menuItem1:
                            MenuItem1View()
                                .onChange(of: router.routeStack) { oldStack, newStack in
                                    // 检查当前视图是否从路由栈中被移除
                                    if oldStack.contains(.menuItem1) && !newStack.contains(.menuItem1) {
                                        // 视图被真正dismiss,执行地图状态更新逻辑
                                        updateMapState(for: .menuItem1, isActive: false)
                                    }
                                }
                        case .menuItem2:
                            MenuItem2View()
                                .onChange(of: router.routeStack) { oldStack, newStack in
                                    if oldStack.contains(.menuItem2) && !newStack.contains(.menuItem2) {
                                        updateMapState(for: .menuItem2, isActive: false)
                                    }
                                }
                        case .detailView(let id):
                            DetailView(id: id)
                        }
                    }
            }
        }
    }
    
    private func updateMapState(for route: Route, isActive: Bool) {
        // 这里处理地图状态更新
    }
}

方案2:利用task(id:)修饰符监听视图生命周期

task(id:)会在视图出现时启动任务,当视图被真正dismiss(而非临时覆盖)时,任务会被自动取消。你可以在任务的onCancel回调中处理dismiss逻辑。

示例代码:

struct MenuItem1View: View {
    var onDismiss: () -> Void
    
    var body: some View {
        Text("菜单1视图")
            .task(id: UUID()) { // 用UUID确保每次视图初始化都创建新任务
                await withTaskCancellationHandler {
                    // 视图存在期间的逻辑(可选)
                } onCancel: {
                    // 视图被真正dismiss时触发
                    onDismiss()
                }
            }
    }
}

// 在导航目标中使用
.navigationDestination(for: Route.self) { route in
    switch route {
    case .menuItem1:
        MenuItem1View {
            // 执行地图状态更新
            updateMapState(for: .menuItem1, isActive: false)
        }
    // ...其他视图
    }
}

这个方案无需维护全局路由状态,每个视图自己处理dismiss回调,适合独立视图的场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 09:05:14