You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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
    }
}

关键注意点

  1. 为什么原来的backBarButtonItem方案无效?
    系统的backBarButtonItem前一个VC的属性,用来定义当当前VC被push后,导航栏显示的返回按钮样式。你在当前VC修改它,相当于修改了一个不属于自己的控件,自然不会有效果。

  2. weak self避免循环引用:
    在UIAlertAction的闭包中必须使用[weak self],否则会导致VC无法被释放,造成内存泄漏。

  3. 保存后重置标记:
    保存完成后记得把hasUnsavedChanges设为false,避免下次返回时误触发弹窗。

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

火山引擎 最新活动