iOS侧滑pop当前viewController时添加确认提示的异常问题求解
问题根因
你当前仅重写了popViewController(animated:)方法,未适配系统自带的侧滑返回手势interactivePopGestureRecognizer的拦截逻辑。侧滑手势触发时,系统已经启动了pop交互流程,此时你返回nil中断pop操作会导致手势状态无法正常重置,导航栈与界面渲染状态不一致,最终出现生命周期方法调用异常、界面冻结的问题。
修复方案
1. 自定义导航控制器适配侧滑手势
让自定义导航控制器遵守UIGestureRecognizerDelegate协议,同步处理手势触发的拦截逻辑,修改后代码如下:
class MyCustomNavController: UINavigationController { var popHandler: (() -> Bool)? override func viewDidLoad() { super.viewDidLoad() interactivePopGestureRecognizer?.delegate = self } override func popViewController(animated: Bool) -> UIViewController? { guard popHandler?() != false else { return nil } popHandler = nil return super.popViewController(animated: animated) } // 每次push新控制器时重置旧的popHandler,避免不同页面逻辑互相干扰 override func pushViewController(_ viewController: UIViewController, animated: Bool) { popHandler = nil super.pushViewController(viewController, animated: animated) } } extension MyCustomNavController: UIGestureRecognizerDelegate { func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { // 仅处理侧滑返回手势 guard gestureRecognizer == interactivePopGestureRecognizer else { return true } // 导航栈内只有一个控制器时不允许侧滑返回 guard viewControllers.count > 1 else { return false } // 同步使用popHandler的逻辑判断是否允许手势触发 return popHandler?() ?? true } }
2. 修正编辑页面的popHandler逻辑
手动触发pop前先清空popHandler,避免重复触发拦截逻辑,修改后代码如下:
func setupPopHandler() { guard let navigationController = self.navigationController as? MyCustomNavController else { return } navigationController.popHandler = { [weak self] in guard let self = self else { return true } if self.hasChangedFields(), self.hasUnsavedWork == true { let alert = UIAlertController(title: "提示", message: "你有未保存的修改", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "保存", style: .default, handler: { [weak self] action in guard let self = self else { return } // 此处执行你的保存逻辑,保存完成后手动pop self.navigationController?.popHandler = nil self.navigationController?.popViewController(animated: true) })) alert.addAction(UIAlertAction(title: "放弃修改", style: .destructive, handler: { [weak self] action in guard let self = self else { return } self.hasUnsavedWork = false // 手动pop前清空popHandler避免重复拦截 self.navigationController?.popHandler = nil self.navigationController?.popViewController(animated: true) })) alert.addAction(UIAlertAction(title: "取消", style: .cancel)) self.present(alert, animated: true) return false } else { return true } } }
优化建议
- 如果存在多个需要拦截pop的页面,建议将popHandler作为UIViewController的扩展属性,导航控制器触发拦截时读取当前topViewController的对应属性,避免全局popHandler被覆盖的问题。
- 编辑页面
viewDidDisappear时可主动清空导航控制器的popHandler,避免野指针调用。
内容的提问来源于stack exchange,提问作者Meeshoo
相关产品推荐
相关产品推荐

