isMovingToParentとisMovingFromParent

f:id:bamboohero:20210608022748p:plain

UIViewControllerのisMovingToParentisMovingFromParentがどのようなときにtrue or falseになるかがわからなかったので調べてみました。



それぞれの定義

isMovingToParent

A Boolean value indicating whether the view controller is being moved to a parent view controller.

https://developer.apple.com/documentation/uikit/uiviewcontroller/2097561-ismovingtoparent

ViewControllerが親ViewControllerに移動したかどうかを示すとのこと。例えば、あるViewControllerがNavigationController配下に加わったらtrueになるということか。


isMovingFromParent

A Boolean value indicating whether the view controller is being removed from a parent view controller.

https://developer.apple.com/documentation/uikit/uiviewcontroller/2097565-ismovingfromparent

ViewControllerが親ViewControllerから削除されたかどうかを示すとのこと。例えば、あるViewControllerがNavigationController配下から削除されたらtrueになるということか。


検証してみた

1つのUINavigationControllerと3つのUIViewControllerを用意します。

UINavigationControllerのルートViewControllerとしてRootViewControllerがあり、ここからプッシュ遷移した先がPushViewController、モーダル遷移した先がModalViewControllerとします。

RootViewControllerとPushViewControllerはUINavigationControllerの子ViewControllerとなります。一方、ModalViewControllerは親子関係がありません。

UINavigationController
├── RootViewController --- modal表示 ---> ModalViewController
    ├── PushViewController


各ライフサイクルメソッドにisMovingToParentisMovingFromParentをprintする処理を仕込み、値がどうなるか検証しました。

まず、RootViewControllerを表示したときの出力です。
viewWillAppearの時点でisMovingToParentがtrueになっています。UINavigationControllerの子ViewControllerとして追加されたためですね。

RootViewController viewDidLoad isMovingToParent: false
RootViewController viewDidLoad isMovingFromParent: false
RootViewController viewWillAppear isMovingToParent: true
RootViewController viewWillAppear isMovingFromParent: false
RootViewController viewDidAppear isMovingToParent: true
RootViewController viewDidAppear isMovingFromParent: false


次に、ModalViewControllerを表示したときの出力です。
ModalViewControllerに親子関係は無いため、いずれの値もfalseです。

ModalViewController viewDidLoad isMovingToParent: false
ModalViewController viewDidLoad isMovingFromParent: false
ModalViewController viewWillAppear isMovingToParent: false
ModalViewController viewWillAppear isMovingFromParent: false
ModalViewController viewDidAppear isMovingToParent: false
ModalViewController viewDidAppear isMovingFromParent: false

モーダルを閉じたときもfalseのままです。

ModalViewController viewWillDisappear isMovingToParent: false
ModalViewController viewWillDisappear isMovingFromParent: false
ModalViewController viewDidDisappear isMovingToParent: false
ModalViewController viewDidDisappear isMovingFromParent: false


次に、PushViewControllerを表示したときの出力です。
viewWillAppearの時点でisMovingToParentがtrueになっています。UINavigationControllerの子ViewControllerとして追加されたためですね。
遷移元のRootViewControllerは非表示状態になりますが、UINavigationControllerのスタックには残ったままなのでisMovingFromParentはfalseのままになっています。

PushViewController viewDidLoad isMovingToParent: false
PushViewController viewDidLoad isMovingFromParent: false
RootViewController viewWillDisappear isMovingToParent: false
RootViewController viewWillDisappear isMovingFromParent: false
PushViewController viewWillAppear isMovingToParent: true
PushViewController viewWillAppear isMovingFromParent: false
RootViewController viewDidDisappear isMovingToParent: false
RootViewController viewDidDisappear isMovingFromParent: false
PushViewController viewDidAppear isMovingToParent: true
PushViewController viewDidAppear isMovingFromParent: false


最後に、PushViewControllerからPushViewControllerに戻ったときの出力です。
PushViewControllerがUINavigationControllerのスタックから削除されるため、viewWillDisappearの時点でisMovingFromParentがtrueになっています。
遷移元のRootViewControllerが表示されますが、UINavigationControllerのスタックにいる状態は変わらないので、isMovingToParentisMovingFromParentはいずれもfalseのままです。

PushViewController viewWillDisappear isMovingToParent: false
PushViewController viewWillDisappear isMovingFromParent: true
RootViewController viewWillAppear isMovingToParent: false
RootViewController viewWillAppear isMovingFromParent: false
PushViewController viewDidDisappear isMovingToParent: false
PushViewController viewDidDisappear isMovingFromParent: true
RootViewController viewDidAppear isMovingToParent: false
RootViewController viewDidAppear isMovingFromParent: false


まとめ

ドキュメントに記載の通りですが、以下が確認できました。

  • isMovingToParent: 親ViewControllerに追加されるときにtrueになる
  • isMovingFromParent: 親ViewControllerから削除されるときにtrueになる

なお、isMovingToParentはviewDidLoadではなくviewWillAppear以降のライフサイクルでtrueになるというのは注意しておきたいですね。

検証コード

RootViewController

import UIKit

class RootViewController: UIViewController {
    lazy var modalButton: UIButton = {
        let button = UIButton(frame: .init(x: 0, y: 0, width: 100, height: 40))
        button.setTitle("modal", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        button.addTarget(self, action: #selector(modal), for: .touchUpInside)
        return button
    }()

    lazy var pushButton: UIButton = {
        let button = UIButton(frame: .init(x: 0, y: 0, width: 100, height: 40))
        button.setTitle("push", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        button.addTarget(self, action: #selector(push), for: .touchUpInside)
        return button
    }()

    lazy var stack: UIStackView = {
        let stack = UIStackView(frame: .init(x: 0, y: 0, width: 100, height: 120))
        stack.axis = .vertical
        stack.addArrangedSubview(modalButton)
        stack.addArrangedSubview(pushButton)
        return stack
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        view.addSubview(stack)

        print("RootViewController viewDidLoad isMovingToParent: \(isMovingToParent)")
        print("RootViewController viewDidLoad isMovingFromParent: \(isMovingFromParent)")
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        stack.center = view.center
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("RootViewController viewWillAppear isMovingToParent: \(isMovingToParent)")
        print("RootViewController viewWillAppear isMovingFromParent: \(isMovingFromParent)")
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("RootViewController viewDidAppear isMovingToParent: \(isMovingToParent)")
        print("RootViewController viewDidAppear isMovingFromParent: \(isMovingFromParent)")
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        print("RootViewController viewWillDisappear isMovingToParent: \(isMovingToParent)")
        print("RootViewController viewWillDisappear isMovingFromParent: \(isMovingFromParent)")
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        print("RootViewController viewDidDisappear isMovingToParent: \(isMovingToParent)")
        print("RootViewController viewDidDisappear isMovingFromParent: \(isMovingFromParent)")
    }

    @objc func modal() {
        present(ModalViewController(), animated: true)
    }

    @objc func push() {
        navigationController?.pushViewController(PushViewController(), animated: true)
    }
}


ModalViewController

import UIKit

class ModalViewController: UIViewController {
    lazy var label: UILabel = {
        let label = UILabel(frame: .init(x: 0, y: 0, width: 100, height: 100))
        label.text = "Modal"
        label.tintColor = .black
        label.textAlignment = .center
        return label
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        view.addSubview(label)

        print("ModalViewController viewDidLoad isMovingToParent: \(isMovingToParent)")
        print("ModalViewController viewDidLoad isMovingFromParent: \(isMovingFromParent)")
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        label.center = view.center
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("ModalViewController viewWillAppear isMovingToParent: \(isMovingToParent)")
        print("ModalViewController viewWillAppear isMovingFromParent: \(isMovingFromParent)")
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("ModalViewController viewDidAppear isMovingToParent: \(isMovingToParent)")
        print("ModalViewController viewDidAppear isMovingFromParent: \(isMovingFromParent)")
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        print("ModalViewController viewWillDisappear isMovingToParent: \(isMovingToParent)")
        print("ModalViewController viewWillDisappear isMovingFromParent: \(isMovingFromParent)")
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        print("ModalViewController viewDidDisappear isMovingToParent: \(isMovingToParent)")
        print("ModalViewController viewDidDisappear isMovingFromParent: \(isMovingFromParent)")
    }
}


PushViewController

import UIKit

class PushViewController: UIViewController {
    lazy var label: UILabel = {
        let label = UILabel(frame: .init(x: 0, y: 0, width: 100, height: 100))
        label.text = "Push"
        label.tintColor = .black
        label.textAlignment = .center
        return label
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        view.addSubview(label)

        print("PushViewController viewDidLoad isMovingToParent: \(isMovingToParent)")
        print("PushViewController viewDidLoad isMovingFromParent: \(isMovingFromParent)")
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        label.center = view.center
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("PushViewController viewWillAppear isMovingToParent: \(isMovingToParent)")
        print("PushViewController viewWillAppear isMovingFromParent: \(isMovingFromParent)")
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("PushViewController viewDidAppear isMovingToParent: \(isMovingToParent)")
        print("PushViewController viewDidAppear isMovingFromParent: \(isMovingFromParent)")
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        print("PushViewController viewWillDisappear isMovingToParent: \(isMovingToParent)")
        print("PushViewController viewWillDisappear isMovingFromParent: \(isMovingFromParent)")
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        print("PushViewController viewDidDisappear isMovingToParent: \(isMovingToParent)")
        print("PushViewController viewDidDisappear isMovingFromParent: \(isMovingFromParent)")
    }
}