UIViewControllerのisMovingToParent
とisMovingFromParent
がどのようなときに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
各ライフサイクルメソッドにisMovingToParent
とisMovingFromParent
を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のスタックにいる状態は変わらないので、isMovingToParent
とisMovingFromParent
はいずれも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)") } }