はじめに
ナビゲーションバーの戻るボタンをタップ時に、「ダイアログを表示 -> キャンセルの場合、遷移させない」という処理を実装したかったのですが、デフォルトで実装されている戻るボタン (backBarButtonItem) をカスタマイズする方法ではうまくいきませんでした。
ダメだった方法
// MARK: onTapBackButton は呼ばれない
let backButton = UIBarButtonItem(image: chevronLeftImage, style: .plain, target: self, action: #selector(onTapBackButton(_:)))
navigationItem.backBarButtonItem = backButton
そこでデフォルトの戻るボタンは使わず、新たにボタンを作成し leftBarButtonItem として配置するという方法で実装しました。
環境
- Xcode 11.5
 - Swift 5.2.4
 
デフォルトの戻るボタンを置き換える
特定の ViewController のみボタンを置き換えた場合、デフォルトの戻るボタンと見た目が変わってしまうため、今回は全てのボタンを置き換えます。
すべての画面で読み込む親クラスとして CommonViewController を生成し実装していきます。
コード全体
class CommonViewController: UIViewController {
	override func viewDidLoad() {
        super.viewDidLoad()
  
        backButton()
    }
 
    private func backButton() {
        let currentViewController = String(describing: type(of: self))
        guard !currentViewController == "FirstViewController" else {
            return
        }
	
        let chevronLeftImage: UIImage? = UIImage(systemName: "chevron.left")
        let backButton = UIBarButtonItem(image: chevronLeftImage, style: .plain, target: self, action: #selector(onTapBackButton(_:)))
        navigationItem.leftBarButtonItem = backButton
    }
 	
    @objc
    func onTapBackButton(_ sender: UIBarButtonItem) {
  	    navigationController?.popViewController(animated: true)
    }
}
最初の画面 (FirstViewController) では戻るボタンを非表示にします。
// 現在の ViewController 名を取得
let currentViewController = String(describing: type(of: self))
guard !currentViewController == "FirstViewController" else {
	return
}
新たにボタンを生成し leftBarButtonItem に登録します。
leftBarButtonItem が表示されている場合、デフォルトの戻るボタンは非表示になります。
let chevronLeftImage: UIImage? = UIImage(systemName: "chevron.left")
let backButton = UIBarButtonItem(image: chevronLeftImage, style: .plain, target: self, action: #selector(onTapBackButton(_:)))
navigationItem.leftBarButtonItem = backButton
タップ時に呼び出されるアクションに、前画面へ遷移する処理を記述します。
@objc
func onTapBackButton(_ sender: UIBarButtonItem) {
	navigationController?.popViewController(animated: true)
}
特定の ViewController で処理をカスタマイズする
処理をカスタマイズしたい ViewController で onTapBackButton を override します。
@objc
override func onTapBackButton(_ sender: UIBarButtonItem) {
	// TODO: ここに処理を記述
 	navigationController?.popViewController(animated: true)
}
参考
- [Xcode - navigationBarの戻るボタンでの画面遷移をキャンセルしたい|teratail] (https://teratail.com/questions/274061)
 - [ios - Execute action when back bar button of UINavigationController is pressed - Stack Overflow] (https://stackoverflow.com/questions/27713747/execute-action-when-back-bar-button-of-uinavigationcontroller-is-pressed/37230710)