addChildViewControllerは実際には何をしますか?


102

私は、iOSの開発に初めて足を浸すことだ、と私はやって持っていた最初のものの一つが実装され、カスタムコンテナビューコントローラを -それを呼び出すことができますSideBarViewController-スワップそれいくつかの可能な子ビューコントローラのどの出ていることは、標準のTab Bar Controllerとほぼ同じです。(これはほとんどタブバーコントローラーですが、タブバーの代わりに非表示のサイドメニューがあります。)

Appleのドキュメントの指示に従ってaddChildViewController、子ViewControllerをコンテナに追加するたびに呼び出します。表示されている現在の子ビューコントローラーを交換するための私のコードは、SideBarViewController次のようになります。

- (void)showViewController:(UIViewController *)newViewController {
    UIViewController* oldViewController = [self.childViewControllers 
                                           objectAtIndex:0];
    
    [oldViewController removeFromParentViewController];
    [oldViewController.view removeFromSuperview];
    
    newViewController.view.frame = CGRectMake(
        0, 0, self.view.frame.size.width, self.view.frame.size.height
    );
    [self addChildViewController: newViewController];
    [self.view addSubview: newViewController.view];
}

それから私addChildViewControllerはここで何をしているのかを理解しようとし始めました、そして私は自分が何も知らないことに気付きました。アレイに新しいものViewControllerを貼り付ける以外に.childViewControllers、何にも影響がないようです。子コントローラーのビューからストーリーボードに設定した子コントローラーへのアクションとアウトレットは、を呼び出さなくても正常に機能しaddChildViewController、他に何が影響するか想像できません。

実際、を呼び出さないようにコードを書き直した場合addChildViewController、代わりに次のようになります...

- (void)showViewController:(UIViewController *)newViewController {

    // Get the current child from a member variable of `SideBarViewController`
    UIViewController* oldViewController = currentChildViewController;

    [oldViewController.view removeFromSuperview];

    newViewController.view.frame = CGRectMake(
        0, 0, self.view.frame.size.width, self.view.frame.size.height
    );
    [self.view addSubview: newViewController.view];

    currentChildViewController = newViewController;
}

...それでも私のアプリは完璧に機能します。

Appleのドキュメントは何をするのかaddChildViewController、なぜ私たちがそれを呼ばなければならないのかについて多くのことを明らかにしていません。現在、メソッドの機能またはUIViewControllerクラスリファレンスのセクションでメソッドを使用する理由の関連する説明の全体は、次のとおりです。

指定されたビューコントローラを子として追加します。...このメソッドは、カスタムコンテナビューコントローラの実装によって呼び出されることのみを目的としています。このメソッドをオーバーライドする場合は、実装でsuperを呼び出す必要があります。

同じページの前半にもこの段落があります。

コンテナビューコントローラは、子のルートビューをビュー階層に追加する前に、子ビューコントローラをそれ自体に関連付ける必要があります。これにより、iOSはイベントを子ビューコントローラーとそれらのコントローラーが管理するビューに適切にルーティングできます。同様に、子のルートビューをビュー階層から削除した後、子ビューコントローラをそれ自体から切断する必要があります。これらの関連付けを作成または解除するために、コンテナは基本クラスで定義された特定のメソッドを呼び出します。これらのメソッドは、コンテナークラスのクライアントによって呼び出されることを意図していません。これらは、コンテナーの実装でのみ使用され、期待される封じ込め動作を提供します。

呼び出す必要のある可能性のある基本的なメソッドは次のとおりです。

addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:

しかし、それはそれが話している「イベント」または「予想される封じ込め動作」が何であるか、またはこれらのメソッドの呼び出しが「必須」である理由(または場合でも)についての手掛かりを提供しません。

Appleドキュメントの「カスタムコンテナービューコントローラー」セクションにあるカスタムコンテナービューコントローラーの例では、すべてこのメソッドを呼び出しているため、子ViewControllerを配列にポップするだけでなく、いくつかの重要な目的に役立つと思いますが、わかりません。その目的は何ですか。このメソッドは何をするのですか、なぜそれを呼び出す必要があるのですか?


3
Appleの2011 WWDCビデオページには、このトピックに関する素晴らしいセッション(「UIViewController包含の実装」)があります。
Alladinian 2013年

回答:


94

私もこの疑問について考えていました。WWDC 2011のビデオのセッション102を見て、View ControllerのBruce D. Nilo氏は次のように述べています。

viewWillAppear:viewDidAppear:などは、とは関係ありませんaddChildViewController:。そのすべてaddChildViewController:ませんが、「このビューコントローラが1つの子である」と、それはビューの外観とは何の関係もないと言うことです。それらが呼び出されたときは、ビューがウィンドウ階層の内外に移動したときに関連付けられます。

したがって、への呼び出しaddChildViewController:はほとんど機能しないようです。呼び出しの副作用は重要な部分です。彼らはparentViewControllerとのchildViewControllers関係から来ています。ここに私が知っている副作用のいくつかがあります:

  • 外観メソッドを子View Controllerに転送する
  • 転送ローテーション方式
  • (おそらく)メモリ警告を転送する
  • 特に一貫性のないVC階層の回避 transitionFromViewController:toViewController:…両方のVCが同じ親を持つ必要がある場合に
  • カスタムコンテナービューコントローラーが状態の保存と復元に参加できるようにする
  • レスポンダーチェーンに参加する
  • フックアップnavigationControllertabBarControllerなどの特性を

セッションは101ではなく102です
SeanChense

レスポンダーチェーンの+1。あなたが子供のUIViewControllerが所有するサブビューにタッチイベントを受信したい場合はaddChildViewControllerが必要です
charlieb

108

例は千の言葉に値すると思います。

私はライブラリアプリで作業していて、ユーザーがメモを追加したいときに表示される素敵なメモ帳ビューを表示したいと考えていました。

ここに画像の説明を入力してください

いくつかの解決策を試した後、私はメモ帳を表示するための独自のカスタムソリューションを発明することになりました。したがって、メモ帳を表示したい場合は、新しいインスタンスを作成しますNotepadViewControllerそのルートビューをサブビューとしてメインビューに追加します。ここまでは順調ですね。

次に、ノートパッドの画像が横長モードでキーボードの下に部分的に隠れていることに気付きました。

ここに画像の説明を入力してください

だからメモ帳の画像を変えて上にシフトしたかった。そのために、willAnimateRotationToInterfaceOrientation:duration:メソッドに適切なコードを記述しましたが、アプリを実行しても何も起こりませんでした。デバッグ後、UIViewControllerの回転メソッドが実際に呼び出されていないことに気付きましたNotepadViewController。メインビューコントローラー内のメソッドのみが呼び出されています。

これを解決するには、すべてのメソッドを NotepadViewControllerは、メインビューコントローラーで呼び出されたときに、手動でました。これはすぐに物事を複雑にし、アプリ内の無関係なコンポーネント間の追加の依存関係を作成します。

これは、子ビューコントローラの概念が導入される前の過去のものです。しかし今は addChildViewController、メインのビューコントローラーにアクセスするだけで、手動での作業を行わなくても、すべてが期待どおりに機能します。

編集: 子ビューコントローラーに転送されるイベントには2つのカテゴリがあります。

1-出現方法:

- viewWillAppear:
- viewDidAppear:
- viewWillDisappear:
- viewDidDisappear:

2-回転方法:

- willRotateToInterfaceOrientation:duration:
- willAnimateRotationToInterfaceOrientation:duration:
- didRotateFromInterfaceOrientation:

shouldAutomaticallyForwardRotationMethodsおよびをオーバーライドすることにより、自動的に転送するイベントカテゴリを制御することもできますshouldAutomaticallyForwardAppearanceMethods


ドキュメントから、そして簡単なテストを行った後addChildViewController、親コントローラーに転送された場合にのみ転送される他のイベントはないと思います。
Hejazi 2013年

自動的にそれを望む転送viewWillLayoutSubviews
MobileMon

10

-[UIViewController addChildViewController:]渡されたView Controllerを、ViewController(親)が参照を維持したいViewControllersの配列に追加するだけです。実際にそれらのviewControllerのビューを別のビュー(たとえば、parentViewControllerのビュー)のサブビューとして追加して、自分で画面に追加する必要があります。ストーリーボードでchildrenViewControllersを使用するためのインターフェイスビルダーの便利なオブジェクトもあります。

以前は、ビューを使用した他のviewControllerの参照を保持するために、@ propertiesにそれらの手動参照を保持する必要がありました。のような組み込みプロパティを持つことは、childViewControllersそのようparentViewControllerな相互作用を管理し、iPadアプリで見つかるUISplitViewControllerのような構成されたviewControllerを構築するための便利な方法です。

さらに、childrenViewControllersは、親が受け取るすべてのシステムイベント(-viewWillAppear、-viewWillDisappearなど)も自動的に受け取ります。以前は、「childrenViewControllers」でこのメソッドを手動で呼び出す必要がありました。

それでおしまい。


それがすべてだと考える根拠は何ですか?また、子供が受け取る「システムイベント」のリストを提供できますか?グーグルで検索してiOS "system events"もそれほどスローアップしない。それはAppleが使っている用語ではないように見えるのですか?
Mark Amery 2013年

これは基本的には、View Controller BのビューをView Controller Aのサブビューとして追加できる便利な方法ですが、View Controller Bでそのビューを管理できます。これが正しく機能するためには、View Controller Bがシステムイベントを取得していることを確認する必要があります(UIViewControllerDelegateコールバックを読み取ります)。'addChildViewController'はこれをフックして、すべてを手動で渡す手間を省きます。
Sam Clewlow 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.