如何利用ViewWillDisappear取消视图消失并阻止NavigationController返回?
嘿,这个场景我太熟了!之前用viewWillDisappear确实踩过坑——等这个方法触发的时候,返回动作已经启动了,根本拦不住。给你几个实用的解决方案,按需选:
方案1:自定义导航栏返回按钮(最省心的可控方案)
直接替换系统默认的返回按钮,把点击逻辑完全握在自己手里。优点是简单粗暴,想怎么拦就怎么拦,缺点是需要自己调整按钮样式,尽量贴近系统风格。
override func viewDidLoad() { super.viewDidLoad() // 自定义返回按钮,样式可以根据需求调整 let backButton = UIBarButtonItem(title: "返回", style: .plain, target: self, action: #selector(backButtonTapped)) // 可选:设置按钮颜色和系统一致 backButton.tintColor = .systemBlue navigationItem.leftBarButtonItem = backButton } @objc private func backButtonTapped() { // 这里写你的条件检查逻辑,比如判断是否有未保存内容 guard !hasUnsavedChanges else { // 条件不满足,弹出提醒 let alert = UIAlertController(title: "注意", message: "你的操作内容将不会保存,确定要返回吗?", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "取消", style: .cancel)) alert.addAction(UIAlertAction(title: "确定返回", style: .destructive) { [weak self] _ in // 用户确认后再执行返回 self?.navigationController?.popViewController(animated: true) }) present(alert, animated: true) return } // 条件满足,直接返回 navigationController?.popViewController(animated: true) } // 示例:模拟未保存状态的属性 private var hasUnsavedChanges: Bool { // 这里替换成你的实际检查逻辑 return true }
方案2:拦截系统返回+侧滑返回(用导航代理)
如果需要拦截系统默认返回按钮和侧滑返回手势,就用UINavigationControllerDelegate的方法。这个方案能覆盖所有返回场景,但要注意处理导航栈的状态,避免界面异常。
class YourViewController: UIViewController, UINavigationControllerDelegate { private var shouldPreventPop = false private var hasUnsavedChanges: Bool { true } // 替换为你的检查逻辑 override func viewDidLoad() { super.viewDidLoad() // 设置当前VC为导航代理 navigationController?.delegate = self } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // 如果是被拦截的返回,重置状态 if shouldPreventPop { shouldPreventPop = false } else { // 正常离开页面时,记得清空代理,避免循环引用 navigationController?.delegate = nil } } func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { // 判断是否是从当前页面返回上一页 let isPoppingFromCurrentVC = navigationController.viewControllers.contains(self) && viewController != self guard isPoppingFromCurrentVC else { return } // 检查条件 if hasUnsavedChanges { // 阻止默认返回动作:重置导航栈为只有当前VC shouldPreventPop = true navigationController.viewControllers = [self] // 弹出提醒框 let alert = UIAlertController(title: "注意", message: "你的操作内容将不会保存,确定要返回吗?", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "取消", style: .cancel)) alert.addAction(UIAlertAction(title: "确定返回", style: .destructive) { [weak self] _ in self?.navigationController?.popViewController(animated: true) }) present(alert, animated: true) } } }
方案3:iOS 13+ 模态场景专属拦截
如果你的页面是用present模态弹出的(不是导航栈push的),iOS13+可以用UIAdaptivePresentationControllerDelegate来拦截返回,这个方法更优雅:
class YourModalViewController: UIViewController, UIAdaptivePresentationControllerDelegate { private var hasUnsavedChanges: Bool { true } // 替换为你的检查逻辑 override func viewDidLoad() { super.viewDidLoad() presentationController?.delegate = self } func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool { // 检查未保存状态 guard !hasUnsavedChanges else { let alert = UIAlertController(title: "注意", message: "你的操作内容将不会保存,确定要返回吗?", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "取消", style: .cancel)) alert.addAction(UIAlertAction(title: "确定返回", style: .destructive) { [weak self] _ in self?.dismiss(animated: true) }) present(alert, animated: true) return false // 返回false阻止默认关闭 } return true // 允许关闭 } }
总结一下
- 只需要拦截点击返回:选方案1,简单可控
- 需要拦截侧滑+系统返回:选方案2,覆盖所有场景
- 模态弹出的页面:iOS13+用方案3更优雅
内容的提问来源于stack exchange,提问作者benjiiiii
相关产品推荐
相关产品推荐

