Was macht addChildViewController eigentlich?


102

Ich tauche gerade zum ersten Mal in die iOS-Entwicklung ein, und eines der ersten Dinge, die ich tun musste, ist die Implementierung eines benutzerdefinierten Container-View-Controllers - nennen wir es SideBarViewController-, der austauscht, welcher von mehreren möglichen untergeordneten View-Controllern es ist zeigt, fast genau wie ein Standard Tab Bar Controller . (Es ist so ziemlich ein Tab Bar Controller, aber mit einem versteckten Seitenmenü anstelle einer Tab Bar.)

addChildViewControllerGemäß den Anweisungen in der Apple-Dokumentation rufe ich jedes Mal auf , wenn ich meinem Container einen untergeordneten ViewController hinzufüge. Mein Code zum Austauschen des aktuellen untergeordneten Ansichts-Controllers SideBarViewControllersieht folgendermaßen aus:

- (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];
}

Dann fing ich an herauszufinden, was addChildViewControllerhier passiert, und mir wurde klar, dass ich keine Ahnung habe. Abgesehen davon, dass das Neue ViewControllerin das .childViewControllersArray eingefügt wird, scheint es keine Auswirkungen auf irgendetwas zu haben. Aktionen und Ausgänge aus der Sicht des untergeordneten Controllers zum untergeordneten Controller, die ich im Storyboard festgelegt habe, funktionieren auch dann noch einwandfrei addChildViewController, wenn ich nie anrufe , und ich kann mir nicht vorstellen, was sich sonst noch darauf auswirken könnte.

In der Tat, wenn ich meinen Code umschreibe, um nicht aufzurufen addChildViewController, und stattdessen so aussehe ...

- (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;
}

... dann funktioniert meine App immer noch perfekt, soweit ich das beurteilen kann!

Die Apple-Dokumentation gibt nicht viel Aufschluss darüber, was addChildViewControllerfunktioniert oder warum wir es nennen sollen. Der gesamte Umfang der relevanten Beschreibung dessen, was die Methode tut oder warum sie in ihrem Abschnitt in der UIViewControllerKlassenreferenz verwendet werden sollte, ist derzeit:

Fügt den angegebenen Ansichts-Controller als untergeordnetes Element hinzu. ... Diese Methode soll nur von einer Implementierung eines benutzerdefinierten Container View Controllers aufgerufen werden. Wenn Sie diese Methode überschreiben, müssen Sie in Ihrer Implementierung super aufrufen.

Es gibt auch diesen Absatz früher auf derselben Seite:

Ihr Container-Ansichts-Controller muss sich einen untergeordneten Ansichts-Controller zuordnen, bevor Sie die Stammansicht des untergeordneten Elements zur Ansichtshierarchie hinzufügen. Auf diese Weise kann iOS Ereignisse ordnungsgemäß an untergeordnete Ansichtscontroller und die von diesen Controllern verwalteten Ansichten weiterleiten. Nachdem die Stammansicht eines Kindes aus seiner Ansichtshierarchie entfernt wurde, sollte der Controller für die untergeordnete Ansicht von sich selbst getrennt werden. Um diese Zuordnungen herzustellen oder zu lösen, ruft Ihr Container bestimmte Methoden auf, die von der Basisklasse definiert wurden. Diese Methoden sollen nicht von Clients Ihrer Containerklasse aufgerufen werden. Sie dürfen nur von der Implementierung Ihres Containers verwendet werden, um das erwartete Containment-Verhalten bereitzustellen.

Hier sind die wesentlichen Methoden, die Sie möglicherweise aufrufen müssen:

addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:

Es gibt jedoch keinen Hinweis darauf, um welche "Ereignisse" oder "erwartetes Eindämmungsverhalten" es sich handelt oder warum (oder sogar wann) das Aufrufen dieser Methoden "wesentlich" ist.

Die Beispiele für benutzerdefinierte Container-Ansichts-Controller im Abschnitt "Benutzerdefinierte Container-Ansichts-Controller" der Apple-Dokumentation rufen alle diese Methode auf. Ich gehe daher davon aus, dass sie einen wichtigen Zweck erfüllt, der über das einfache Platzieren des untergeordneten ViewControllers in einem Array hinausgeht, aber ich kann nicht herausfinden heraus, was dieser Zweck ist. Was macht diese Methode und warum sollte ich sie aufrufen?


3
Auf der WWDC- Videoseite 2011 von Apple finden Sie eine großartige Sitzung ("Implementieren von UIViewController Containment") zu diesem Thema.
Alladinian

Antworten:


94

Ich habe mich auch über diese Frage gewundert. Ich habe mir Session 102 der WWDC 2011- Videos angesehen und Mr. View Controller, Bruce D. Nilo , sagte Folgendes:

viewWillAppear:, viewDidAppear:Etc. hat nichts damit zu tun addChildViewController:. Alles, addChildViewController:was Sie tun müssen, ist zu sagen: "Dieser Ansichts-Controller ist ein untergeordnetes Element von diesem" und hat nichts mit dem Erscheinungsbild der Ansicht zu tun. Wann sie aufgerufen werden, hängt davon ab, wann Ansichten in die Fensterhierarchie ein- und ausgezogen werden.

Es scheint also, dass der Aufruf zu addChildViewController:sehr wenig bewirkt. Die Nebenwirkungen des Anrufs sind der wichtige Teil. Sie kommen aus den parentViewControllerund childViewControllersBeziehungen. Hier sind einige der Nebenwirkungen, die ich kenne:

  • Weiterleiten von Darstellungsmethoden an untergeordnete Ansichtscontroller
  • Weiterleitungsrotationsmethoden
  • (Möglicherweise) Speicherwarnungen weiterleiten
  • Vermeiden inkonsistenter VC-Hierarchien, insbesondere transitionFromViewController:toViewController:…wenn beide VCs dasselbe übergeordnete Element benötigen
  • Zulassen, dass benutzerdefinierte Controller für die Containeransicht an der Statuserhaltung und -wiederherstellung teilnehmen
  • Teilnahme an der Responderkette
  • Einhaken die navigationController, tabBarControllerusw. Eigenschaften

Es ist Sitzung 102 nicht 101
SeanChense

+1 für die Responderkette. addChildViewController ist erforderlich, wenn Sie Berührungsereignisse in einer Unteransicht empfangen möchten, die einem untergeordneten UIViewController gehört
Charlieb

108

Ich denke, ein Beispiel sagt mehr als tausend Worte.

Ich habe an einer Bibliotheks-App gearbeitet und wollte eine schöne Notizblockansicht anzeigen, die angezeigt wird, wenn der Benutzer eine Notiz hinzufügen möchte.

Geben Sie hier die Bildbeschreibung ein

Nachdem ich einige Lösungen ausprobiert hatte, erfand ich schließlich meine eigene benutzerdefinierte Lösung, um den Notizblock anzuzeigen. Wenn ich also den NotepadViewControllerEditor anzeigen möchte, erstelle ich eine neue Instanz von und füge die Stammansicht als Unteransicht zur Hauptansicht hinzu. So weit, ist es gut.

Dann bemerkte ich, dass das Notizblockbild im Querformat teilweise unter der Tastatur versteckt ist.

Geben Sie hier die Bildbeschreibung ein

Also wollte ich das Notizblockbild ändern und es nach oben verschieben. Und dazu habe ich den richtigen Code in willAnimateRotationToInterfaceOrientation:duration:method geschrieben, aber als ich die App ausführte, passierte nichts! Und nach dem Debuggen bemerkte ich, dass keine der UIViewControllerRotationsmethoden tatsächlich aufgerufen wird NotepadViewController. Es werden nur die Methoden im Hauptansichts-Controller aufgerufen.

Um dies zu lösen, musste ich alle Methoden NotepadViewControllermanuell aufrufen, wenn sie im Hauptansichts-Controller aufgerufen wurden. Dies wird die Dinge bald komplizieren und eine zusätzliche Abhängigkeit zwischen nicht verwandten Komponenten in der App schaffen.

Das war in der Vergangenheit, bevor das Konzept der untergeordneten Ansichtssteuerungen eingeführt wurde. Jetzt müssen Sie nur noch addChildViewControllerden Hauptansichts-Controller aufrufen, und alles funktioniert wie erwartet ohne weitere manuelle Arbeit.

Bearbeiten: Es gibt zwei Kategorien von Ereignissen, die an untergeordnete Ansichtscontroller weitergeleitet werden:

1- Erscheinungsmethoden:

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

2- Rotationsmethoden:

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

Sie können auch steuern, welche Ereigniskategorien Sie automatisch weiterleiten möchten, indem Sie shouldAutomaticallyForwardRotationMethodsund überschreiben shouldAutomaticallyForwardAppearanceMethods.


Aus der Dokumentation und nach einem kurzen Test glaube ich nicht, dass es ein anderes Ereignis gibt, das nur weitergeleitet wird, wenn Sie addChildViewControlleran den übergeordneten Controller weitergeleitet werden.
Hejazi

wünschte, es würde automatisch weitergeleitet viewWillLayoutSubviews
MobileMon

10

-[UIViewController addChildViewController:]Fügt nur den übergebenen View-Controller in ein Array von viewControllern ein, auf die ein viewController (das übergeordnete Element) verweisen möchte. Sie sollten die Ansichten von viewController tatsächlich selbst auf dem Bildschirm hinzufügen, indem Sie sie als Unteransichten einer anderen Ansicht hinzufügen (z. B. der Ansicht von parentViewController). In Interface Builder gibt es auch ein praktisches Objekt zur Verwendung von childrenViewControllern in Storyboards.

Zuvor mussten Sie in @properties manuell auf diese viewController verweisen, deren Ansichten Sie verwendet haben. Eine integrierte Eigenschaft wie childViewControllersund folglich parentViewControllerist eine bequeme Möglichkeit, solche Interaktionen zu verwalten und zusammengesetzte viewController wie den UISplitViewController zu erstellen, den Sie auf iPad-Apps finden.

Darüber hinaus empfangen childrenViewController automatisch alle Systemereignisse, die das übergeordnete Element empfängt: -viewWillAppear, -viewWillDisappear usw. Bisher sollten Sie diese Methoden auf Ihren "childrenViewControllern" manuell aufgerufen haben.

Das ist es.


Was ist Ihre Grundlage für die Annahme, dass das alles ist, was es tut? Können Sie auch eine Liste der 'Systemereignisse' bereitstellen, die vom Kind empfangen werden? Eine Google-Suche nach iOS "system events"wirft nicht viel auf. Es scheint kein Begriff zu sein, den Apple verwendet.
Mark Amery

Grundsätzlich handelt es sich um eine praktische Methode, mit der Sie die Ansicht von View Controller B als Unteransicht von View Controller A hinzufügen können, View Controller B jedoch weiterhin die Ansicht verwalten kann. Damit dies ordnungsgemäß funktioniert, müssen Sie sicherstellen, dass View Controller B die Systemereignisse erhält (siehe UIViewControllerDelegate-Rückrufe). 'addChildViewController' verbindet dies für Sie, um Ihnen den Aufwand zu ersparen, alles manuell weiterzugeben.
Sam Clewlow
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.