iOS(iPad)分屏应用导航栏返回按钮未保存数据校验求助
解决导航返回按钮未保存数据校验的问题
你遇到的核心问题是系统返回按钮(backBarButtonItem)的归属逻辑理解错了——这个按钮实际上属于栈里的前一个视图控制器,而不是当前你正在编辑的这个VC!所以你在当前VC里修改它的target和action完全不会生效,因为显示在导航栏上的返回按钮是由上一个VC的navigationItem.backBarButtonItem控制的。
下面给你两种可靠的实现方案,覆盖点击返回按钮和侧滑返回(iPad分屏场景下用户常用)的情况:
方案一:自定义导航栏返回按钮(最直接可控)
直接替换系统的返回按钮为自定义按钮,完全掌控点击事件:
import UIKit class InputViewController: UIViewController { // 标记是否存在未保存数据 private var hasUnsavedChanges = false override func viewDidLoad() { super.viewDidLoad() // 替换系统返回按钮为自定义按钮 setupCustomBackButton() } private func setupCustomBackButton() { // 可以选择和系统风格一致的箭头按钮,或者用文字 let backButton = UIBarButtonItem( image: UIImage(systemName: "arrow.left"), style: .plain, target: self, action: #selector(backButtonTapped) ) // 如果想用文字按钮,就用下面这句替换上面的初始化 // let backButton = UIBarButtonItem(title: "返回", style: .plain, target: self, action: #selector(backButtonTapped)) navigationItem.leftBarButtonItem = backButton } @objc private func backButtonTapped() { checkUnsavedChangesThenDismiss() } private func checkUnsavedChangesThenDismiss() { guard hasUnsavedChanges else { // 无未保存数据,直接返回 navigationController?.popViewController(animated: true) return } // 弹出确认弹窗 let alert = UIAlertController( title: "未保存的修改", message: "你有未保存的数据,是否要保存后再离开?", preferredStyle: .alert ) alert.addAction(UIAlertAction(title: "保存", style: .default) { [weak self] _ in self?.saveData() self?.navigationController?.popViewController(animated: true) }) alert.addAction(UIAlertAction(title: "不保存", style: .destructive) { [weak self] _ in self?.navigationController?.popViewController(animated: true) }) alert.addAction(UIAlertAction(title: "取消", style: .cancel)) present(alert, animated: true) } private func saveData() { // 这里写入你的数据保存逻辑 print("数据已保存") hasUnsavedChanges = false } }
方案二:拦截侧滑返回(覆盖手势操作场景)
iPad分屏应用中用户经常用侧滑手势返回,上面的方案无法拦截这个行为,所以需要结合导航控制器代理来处理:
在上面的代码基础上,添加导航代理实现:
extension InputViewController: UINavigationControllerDelegate { func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { // 判断是否是从当前VC返回上一个VC let isPoppingCurrentVC = viewController != self if isPoppingCurrentVC && hasUnsavedChanges { // 先阻止默认的返回操作 navigationController.popViewController(animated: false) // 调用我们的校验逻辑 checkUnsavedChangesThenDismiss() } } override func viewDidLoad() { super.viewDidLoad() setupCustomBackButton() // 设置导航代理 navigationController?.delegate = self } }
关键注意点
为什么原来的
backBarButtonItem方案无效?
系统的backBarButtonItem是前一个VC的属性,用来定义当当前VC被push后,导航栏显示的返回按钮样式。你在当前VC修改它,相当于修改了一个不属于自己的控件,自然不会有效果。用
weak self避免循环引用:
在UIAlertAction的闭包中必须使用[weak self],否则会导致VC无法被释放,造成内存泄漏。保存后重置标记:
保存完成后记得把hasUnsavedChanges设为false,避免下次返回时误触发弹窗。
内容的提问来源于stack exchange,提问作者carlson




