Cosa fa effettivamente addChildViewController?


102

Sto solo immergendo i miei piedi per la prima volta nello sviluppo iOS e una delle prime cose che ho dovuto fare è stata implementare un controller di visualizzazione del contenitore personalizzato - chiamiamolo SideBarViewController- che sostituisce quale dei diversi possibili controller di visualizzazione figlio è mostra, quasi esattamente come un controller Tab Bar standard . (È praticamente un controller della barra delle schede, ma con un menu laterale nascosta invece di una barra delle schede.)

Secondo le istruzioni nella documentazione Apple, chiamo addChildViewControllerogni volta che aggiungo un ViewController figlio al mio contenitore. Il mio codice per lo scambio del controller di visualizzazione figlio corrente mostrato SideBarViewControllerdall'aspetto simile a questo:

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

Poi ho iniziato a cercare di capire cosa addChildViewControllerfa qui, e mi sono reso conto che non ne avevo idea. Oltre attaccare il nuovo ViewControllernella .childViewControllersmatrice, sembra non avere alcun effetto su nulla. Le azioni e gli sbocchi dal punto di vista del controller figlio al controller figlio che ho impostato sullo storyboard funzionano ancora bene anche se non chiamo mai addChildViewControllere non riesco a immaginare cos'altro potrebbe influenzare.

In effetti, se riscrivo il mio codice per non chiamare addChildViewController, e invece assomiglio a questo ...

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

... allora la mia app funziona ancora perfettamente, per quanto ne so!

La documentazione di Apple non fa molta luce su cosa addChildViewControllerfa o perché dovremmo chiamarlo. L'intera portata della descrizione pertinente di ciò che fa il metodo o del motivo per cui dovrebbe essere utilizzato nella sua sezione nel UIViewControllerriferimento della classe è, al momento:

Aggiunge il controller di visualizzazione specificato come bambino. ... Questo metodo deve essere chiamato solo da un'implementazione di un controller di visualizzazione del contenitore personalizzato. Se sostituisci questo metodo, devi chiamare super nella tua implementazione.

C'è anche questo paragrafo prima nella stessa pagina:

Il controller della vista del contenitore deve associare un controller della vista figlio a se stesso prima di aggiungere la vista radice del figlio alla gerarchia della vista. Ciò consente a iOS di instradare correttamente gli eventi ai controller di visualizzazione figlio e alle visualizzazioni gestite da tali controller. Allo stesso modo, dopo aver rimosso la vista radice di un bambino dalla sua gerarchia di viste, dovrebbe disconnettere quel controller della vista figlio da se stesso. Per creare o interrompere queste associazioni, il contenitore chiama metodi specifici definiti dalla classe base. Questi metodi non sono destinati a essere chiamati dai client della classe contenitore; devono essere utilizzati solo dall'implementazione del contenitore per fornire il comportamento di contenimento previsto.

Ecco i metodi essenziali che potresti dover chiamare:

addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:

ma non offre alcun indizio su cosa siano gli "eventi" o il "comportamento di contenimento previsto" di cui si parla, o perché (o anche quando) chiamare questi metodi sia "essenziale".

Gli esempi di controller di visualizzazione del contenitore personalizzati nella sezione "Controller di visualizzazione del contenitore personalizzati" della documentazione Apple chiamano tutti questo metodo, quindi presumo che serva a uno scopo importante oltre al semplice inserimento del ViewController figlio su un array, ma non riesco a capire scoprire qual è lo scopo. Cosa fa questo metodo e perché dovrei chiamarlo?


3
La pagina dei video WWDC del 2011 di Apple ha un'ottima sessione ("Implementing UIViewController Containment") su questo argomento.
Alladinian,

Risposte:


94

Mi stavo chiedendo anche questa domanda. Ho guardato la sessione 102 dei video del WWDC 2011 e il signor View Controller, Bruce D.Nilo , ha detto questo:

viewWillAppear:, viewDidAppear:, Ecc non hanno nulla a che fare con addChildViewController:. Tutto ciò che addChildViewController:fa è dire "Questo controller di visualizzazione è un figlio di quello" e non ha nulla a che fare con l'aspetto della visualizzazione. Quando vengono chiamati è associato a quando le visualizzazioni entrano ed escono dalla gerarchia della finestra.

Quindi sembra che la chiamata a addChildViewController:fare molto poco. Gli effetti collaterali della chiamata sono la parte importante. Provengono dalle relazioni parentViewControllere childViewControllers. Ecco alcuni degli effetti collaterali che conosco:

  • Inoltro dei metodi di aspetto ai controller di visualizzazione figlio
  • Metodi di rotazione inoltro
  • (Possibilmente) inoltrare gli avvisi di memoria
  • Evitare gerarchie VC incoerenti, soprattutto transitionFromViewController:toViewController:…quando entrambe le VC devono avere lo stesso genitore
  • Consentire ai controller di visualizzazione del contenitore personalizzati di prendere parte alla conservazione e al ripristino dello stato
  • Partecipare alla catena del risponditore
  • Collegamento del navigationController, tabBarController, proprietà, ecc

È la sessione 102, non 101
SeanChense

+1 per la catena del risponditore. addChildViewController è richiesto se desideri ricevere eventi di tocco su una sottoview di proprietà di un bambino UIViewController
charlieb

108

Penso che un esempio valga più di mille parole.

Stavo lavorando su un'app di libreria e volevo mostrare una bella visualizzazione del blocco note che appare quando l'utente vuole aggiungere una nota.

inserisci qui la descrizione dell'immagine

Dopo aver provato alcune soluzioni, ho finito per inventare la mia soluzione personalizzata per mostrare il blocco note. Quindi, quando voglio mostrare il blocco note, creo una nuova istanza di NotepadViewControllere aggiungo la sua vista principale come vista secondaria alla vista principale. Fin qui tutto bene.

Poi ho notato che l'immagine del blocco note è parzialmente nascosta sotto la tastiera in modalità orizzontale.

inserisci qui la descrizione dell'immagine

Quindi ho voluto cambiare l'immagine del blocco note e spostarla verso l'alto. E per farlo, ho scritto il codice corretto nel willAnimateRotationToInterfaceOrientation:duration:metodo, ma quando ho eseguito l'app non è successo niente! E dopo il debug ho notato che nessuno dei UIViewControllermetodi di rotazione di è effettivamente chiamato NotepadViewController. Vengono chiamati solo i metodi nel controller della vista principale.

Per risolvere questo problema, avevo bisogno di chiamare tutti i metodi NotepadViewControllermanualmente quando vengono chiamati nel controller di visualizzazione principale. Questo presto complicherà le cose e creerà una dipendenza extra tra i componenti non correlati nell'app.

Era in passato, prima che venisse introdotto il concetto di controller per la visualizzazione dei bambini. Ma ora, devi solo accedere al addChildViewControllercontroller della vista principale e tutto funzionerà come previsto senza ulteriori lavori manuali.

Modifica: esistono due categorie di eventi che vengono inoltrati ai controller di visualizzazione figlio:

1- Metodi di aspetto:

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

2- Metodi di rotazione:

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

Puoi anche controllare quali categorie di eventi desideri vengano inoltrate automaticamente ignorando shouldAutomaticallyForwardRotationMethodse shouldAutomaticallyForwardAppearanceMethods.


Dalla documentazione e dopo aver fatto un rapido test non credo ci sia nessun altro evento che viene inoltrato solo se addChildViewControlleral controllore genitore.
Hejazi,

desidero che venga inoltrato automaticamente viewWillLayoutSubviews
MobileMon

10

-[UIViewController addChildViewController:]aggiunge solo il controller di visualizzazione passato in una matrice di viewController a cui un viewController (il genitore) vuole mantenere il riferimento. Dovresti effettivamente aggiungere le viste di viewController sullo schermo aggiungendole come sottoviste di un'altra vista (ad esempio la vista del parentViewController). C'è anche un oggetto di convenienza in Interface Builder per usare childrenViewControllers in Storyboards.

In precedenza, per mantenere il riferimento ad altri viewController di cui utilizzavi le viste, dovevi mantenere il riferimento manuale ad essi in @properties. Avere una proprietà incorporata come childViewControllerse di conseguenza parentViewControllerè un modo conveniente per gestire tali interazioni e creare viewController composti come l'UISplitViewController che trovi sulle app per iPad.

Inoltre, childrenViewControllers riceve automaticamente anche tutti gli eventi di sistema che il genitore riceve: -viewWillAppear, -viewWillDisappear, ecc. In precedenza avresti dovuto chiamare questi metodi manualmente sui tuoi "childrenViewControllers".

Questo è tutto.


Qual è la tua base per pensare che è tutto ciò che fa? Inoltre, potete fornire un elenco degli "eventi di sistema" ricevuti dal bambino? Una ricerca su Google iOS "system events"non genera molto; non sembra essere un termine che utilizza Apple?
Mark Amery

È fondamentalmente un metodo pratico che ti consente di aggiungere la vista del controller di visualizzazione B come una vista secondaria del controller di visualizzazione A, ma che il controller di visualizzazione B gestisca la sua visualizzazione. Affinché funzioni correttamente, è necessario assicurarsi che il controller di visualizzazione B riceva gli eventi di sistema (leggi callback UIViewControllerDelegate). 'addChildViewController' lo aggancia per te, per risparmiarti lo sforzo di passare tutto manualmente.
Sam Clewlow
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.