xyk blog

最近は iOS 開発の記事が多めです。

UINavigationController を使って深い階層の ViewController に一気に遷移する方法

検証環境:
Xcode 11.1
Swift 5.1

例えば Universal Links などの機能を使ってディープリンクで深い階層に一気に遷移させたい場合などに使える。
UINavigationController の setViewControllers(_:animated:) というメソッドがあるので、この引数に複数の ViewController を渡せばよい。

developer.apple.com

以下実装例。

まず、RootViewController には ViewControllerAクラスが設定されている。
ここに ViewControllerB、ViewControllerC、ViewControllerD の順に push したのと同じ状態を setViewControllers を使って作る。
pop で最終的に ViewControllerA にまで戻りたいのであれば、表示中の ViewControllerA についても配列の最初に追加する必要がある。

// ViewControllerA
@IBAction func handleButton(_ sender: UIButton) {
    let sb = UIStoryboard(name: "Main", bundle: nil)
    let vcB = sb.instantiateViewController(identifier: "ViewControllerB")
    let vcC = sb.instantiateViewController(identifier: "ViewControllerC")
    let vcD = sb.instantiateViewController(identifier: "ViewControllerD")
    navigationController?.setViewControllers([self, vcB, vcC, vcD], animated: true)
}

用意した遷移ボタン押下すると上記 handleButtonメソッドが呼ばれる。
挙動としては ViewControllerBViewControllerCはスキップし、ViewControllerDが表示される。
この時の ViewController のライフサイクルは、遷移元のViewControllerAViewControllerDのみ呼ばれる。

ViewControllerD:viewDidLoad()
ViewControllerA:viewWillDisappear(_:)
ViewControllerD:viewWillAppear(_:)
ViewControllerA:viewDidDisappear(_:)
ViewControllerD:viewDidAppear(_:)

この状態から戻るボタンで pop させるとViewControllerCの読み込みが開始される。

ViewControllerC:viewDidLoad()
ViewControllerD:viewWillDisappear(_:)
ViewControllerC:viewWillAppear(_:)
ViewControllerD:viewDidDisappear(_:)
ViewControllerC:viewDidAppear(_:)

さらに戻るとViewControllerBが読み込まれる。

ViewControllerB:viewDidLoad()
ViewControllerC:viewWillDisappear(_:)
ViewControllerB:viewWillAppear(_:)
ViewControllerC:viewDidDisappear(_:)
ViewControllerB:viewDidAppear(_:)

さらに戻ると最初のViewControllerAが表示される。

ViewControllerB:viewWillDisappear(_:)
ViewControllerA:viewWillAppear(_:)
ViewControllerB:viewDidDisappear(_:)
ViewControllerA:viewDidAppear(_:)