スキップしてメイン コンテンツに移動

addChildViewController:


はじめに

iOS5からUIViewControllerを自前のクラスにaddChildViewController:メソッドで追加できるようになった。
最近では、こういった自前コンテナは主にFacebookやPath2.0のスライドメニュー風のUI開発なんかに使われていると思う。
このaddChildViewController:メソッドの後には必ずdidMoveToParentViewController:メソッドを呼ぶという説明があるが、これの意味がよくわからなかったのでそのメモ。
例えば
didMoveToParentViewControllerの必要性を疑うコード
//自前コンテナ(self)にchildViewControllerを追加
[self addChildViewController:childViewController];

//その後didMoveToParentViewControllerを実行しなければいけない
[childViewController didMoveToParentViewController:self];
この追加(add)したあとになぜ完了(didMove)を明示的に知らせないといけないのか謎という感覚。また、これがなくてもこちらの想定通り動くのでずっと疑問だった。

結論としては

自前コンテナでaddChildViewController:を実行した後は、やはり必ずdidMoveToParentViewController:を呼ぶ。

説明

UIViewControllerの追加サイクル

そもそもUIViewControllerの追加には「開始」と「終了」がかならずセットで必要となる。
・追加: addChildViewController:
・開始: willMoveToParentViewController:(add後に自動で呼ばれる)
・完了: didMoveToParentViewController:(任意のタイミングで呼ぶ)
まず自前コンテナへの追加のためにaddChildViewController:した際に、ViewControllerの追加開始の合図であるwillMoveToParentViewControler:は自動で呼ばれる。
開始は自動で呼ばれるが、それが終了したことをコールするためのdidMoveToParentViewController:メソッドは自動で呼ばれないため必要となる。

終了をコールしないと何に困るのか

もともとUIViewControllerは自身が他のコンテナに追加(もしくは削除)されている最中かどうかを知ることで処理を分岐する事もできるようになっていて、これを判断するのに完了を呼ぶ必要がある。
ちなみに、追加されている途中かどうかはUIViewControllerのisMovingToParentViewControllerメソッドによって取得できる。

didMoveToParentViewController:がなぜ自動で呼ばれないようになっているか

では、なぜdidMoveToParentViewController:が自動で呼ばれないかというと、画面遷移にトランジションを指定したい場合が想定されているからだと思う。
例えば次のコードのように、完了までに時間がかかる(実装者が時間を指定できる)場合に、完了のメソッドが明示的にコールできるようになっていることでその意味が出てくる。
/*fromViewControllerはすでにコンテナに追加されているとする*/

//toViewControllerをコンテナに追加する
[self addChildViewController:toViewController];

//fromからtoへ画面遷移のトランジションを行う
[self transitionFromViewController:fromViewController
                   toViewController:toViewController
                           duration:1.0
                            options:UIViewAnimationOptionTransitionNone
                         animations:^{
                             // ここで何かしらの遷移アニメーション
                         }
                         completion:^(BOOL finished) {
                             // 追加完了を明示的に行う
                             [toViewController didMoveToParentViewController:self];
                         }];

まとめ

上記のように、画面遷移のトランジションを使わなくてもaddChildViewController:したらdidMoveToParentViewController:を呼び出すことを忘れないようにしたらよいでしょう。
もし、突っ込みどころ等あればコメントなり編集リクエストでお願いします。

参考

isMovingToParentViewControllerメソッドについてはiOS View Controllerカタログの「ナビゲーションスタックの変化の監視」
https://developer.apple.com/jp/devcenter/ios/library/documentation/ViewControllerCatalog.pdf

コメント

このブログの人気の投稿

日本に来た 今三週間

四月六日に日本に来た、今三週間になった。 嬉しくなかった、悲しくなかった。 日本の携帯電話のカメラはとてもいいよ、私は今撮影が好きだ、いつも寮に帰る途中で花とか魚とか写真を撮る。これも生活の趣味だ。 滴は綺麗だ 桜が凋んで落ちる 天気がいいとき、自転車に乗って、食材を買った。 自分で料理をつくる、おいしそうでしょう。 昨日清水寺に行った、天気はとても熱いね。日に焼けすぎたので、腕は今赤い。 側に花があるので、赤い清水寺はもっと美しそうだ。 木の色は艶やかだ。