SwiftUI中如何在视图从自定义导航栈弹出时立即执行函数?
全屏滑动返回时,视图弹出后立即执行指定函数
问题背景
现有自定义导航栈实现了全屏左右滑动返回功能,需要在视图完全弹出导航栈后立即执行特定函数,要求不能在滑动中途或用户松手前触发。已知onDisappear方法存在延迟,无法满足实时触发的需求。
解决方案
核心思路是监听自定义滑动手势的结束状态,结合导航栈的变化判断是否完成pop操作,再触发指定函数。以下是修改后的完整代码:
完整修改后代码
import SwiftUI struct ContentView: View { @State private var isEnabled: Bool = true var body: some View { FullSwipeNavigationStack { NavigationLink("全屏滑动返回视图") { Text("Hello") .enableFullSwipePop(isEnabled) { // 这里放置视图弹出后需要执行的逻辑 print("视图已完全弹出,执行指定函数") } } } } } struct FullSwipeNavigationStack<Content: View>: View { @ViewBuilder var content: Content @State private var customGesture: UIPanGestureRecognizer = { let gesture = UIPanGestureRecognizer() gesture.name = UUID().uuidString gesture.isEnabled = false return gesture }() var body: some View { NavigationStack { content .background { AttachGestureView(gesture: $customGesture) } } .environment(\.popGestureID, customGesture.name) .onReceive(NotificationCenter.default.publisher(for: .init(customGesture.name ?? "")), perform: { info in if let userInfo = info.userInfo, let status = userInfo["status"] as? Bool { customGesture.isEnabled = status } }) } } extension View { @ViewBuilder func enableFullSwipePop(_ isEnabled: Bool, onPopCompleted: (() -> Void)? = nil) -> some View { self .modifier(FullSwipeModifier(isEnabled: isEnabled, onPopCompleted: onPopCompleted)) } } fileprivate struct PopNotificationID: EnvironmentKey { static var defaultValue: String? } fileprivate extension EnvironmentValues { var popGestureID: String? { get { self[PopNotificationID.self] } set { self[PopNotificationID.self] = newValue } } } fileprivate struct FullSwipeModifier: ViewModifier { var isEnabled: Bool let onPopCompleted: (() -> Void)? @Environment(\.popGestureID) private var gestureID @State private var isCurrentTopView = true func body(content: Content) -> some View { content .onChange(of: isEnabled, initial: true) { _, newValue in guard let gestureID = gestureID else { return } NotificationCenter.default.post(name: .init(gestureID), object: nil, userInfo: ["status": newValue]) } .onDisappear { guard let gestureID = gestureID else { return } NotificationCenter.default.post(name: .init(gestureID), object: nil, userInfo: ["status": false]) isCurrentTopView = false } .onAppear { isCurrentTopView = true } .onReceive(NotificationCenter.default.publisher(for: .init("FullSwipePopCompleted"))) { _ in if !isCurrentTopView { onPopCompleted?() } } } } fileprivate struct AttachGestureView: UIViewRepresentable { @Binding var gesture: UIPanGestureRecognizer func makeUIView(context: Context) -> UIView { UIView() } func updateUIView(_ uiView: UIView, context: Context) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.02) { guard let parentVC = uiView.parentViewController, let navVC = parentVC.navigationController else { return } if navVC.view.gestureRecognizers?.first(where: { $0.name == gesture.name }) == nil { navVC.addFullSwipeGesture(gesture) print("手势已添加") } } } } fileprivate extension UINavigationController { func addFullSwipeGesture(_ gesture: UIPanGestureRecognizer) { guard let gestureSelector = interactivePopGestureRecognizer?.value(forKey: "targets") else { return } gesture.setValue(gestureSelector, forKey: "targets") gesture.addTarget(self, action: #selector(handleFullSwipeGesture(_:))) view.addGestureRecognizer(gesture) } @objc private func handleFullSwipeGesture(_ gesture: UIPanGestureRecognizer) { // 仅在手势结束/取消时检查是否完成pop guard gesture.state == .ended || gesture.state == .cancelled else { return } DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { // 发送pop完成通知 NotificationCenter.default.post(name: .init("FullSwipePopCompleted"), object: nil) } } } fileprivate extension UIView { var parentViewController: UIViewController? { sequence(first: self) { $0.next } .first(where: { $0 is UIViewController }) as? UIViewController } }
关键修改说明
- 给自定义滑动手势添加状态监听
handleFullSwipeGesture,仅在手势结束或取消时发送FullSwipePopCompleted通知,避免中途触发 - 扩展
enableFullSwipePop方法,新增onPopCompleted回调参数,用于传入需要执行的函数 - 在
FullSwipeModifier中通过isCurrentTopView标记当前视图是否处于栈顶,结合通知判断视图是否已被完全弹出,确保回调只在正确时机触发 - 延迟0.05秒发送通知是为了确保导航栈完成更新,避免判断逻辑出错
内容的提问来源于stack exchange,提问作者Ahmed Zaidan
相关产品推荐
相关产品推荐

