2013-06-19 7 views
83

Sto solo tuffando i miei piedi per la prima volta nello sviluppo di iOS, e una delle prime cose che ho dovuto fare è implementare un custom container view controller - chiamiamolo SideBarViewController - che scambia quale di diversi possibili controller di vista figlio esso mostra, quasi esattamente come uno standard Controller Barra di Tab. (E 'praticamente un Tab Bar controller ma con un menu laterale hideable invece di una barra delle schede.)Cosa fa effettivamente addChildViewController?

secondo le istruzioni riportate nella documentazione di Apple, che io chiamo addChildViewController ogni volta aggiungo un bambino ViewController al mio contenitore. Il mio codice per scambiare il controller della vista bambino corrente che viene mostrato dal SideBarViewController assomiglia 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 cercando di capire che cosa addChildViewController fa qui, e mi sono reso conto che non ho idea. Oltre ad attaccare il nuovo nell'array .childViewControllers, sembra che non abbia alcun effetto su nulla. Le azioni e le uscite dalla vista del controller figlio al controller figlio che ho impostato sullo storyboard funzionano ancora bene anche se non chiamo mai addChildViewController e non riesco a immaginare cos'altro potrebbe influenzare.

In effetti, se riscrivo il mio codice di non chiamare addChildViewController, e invece simile a questa ...

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

... poi la mia applicazione funziona ancora perfettamente, per quanto posso dire!

La documentazione Apple non fa molta luce su cosa fa addChildViewController o sul motivo per cui dovremmo chiamarlo. L'intera estensione della relativa descrizione di ciò che il metodo fa o perché dovrebbe essere utilizzato nella sua sezione nella UIViewController Class Reference è, allo stato attuale:

aggiunge la controller di vista come un bambino. ... Questo metodo deve essere chiamato solo da un'implementazione di un controller di visualizzazione contenitore personalizzato. Se si sostituisce questo metodo, è necessario chiamare super nella propria implementazione.

C'è anche questo paragrafo in precedenza sulla stessa pagina:

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

Questi sono i metodi essenziali si potrebbe aver bisogno di chiamare:

addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:

ma non offre alcun indizio su ciò che il "eventi" o "comportamento atteso di contenimento" di cui parla o perché (o anche quando) chiamare questi metodi è "essenziale".

Gli esempi di controller di visualizzazione contenitore personalizzati nella sezione "Controller di visualizzazione contenitori personalizzati" della documentazione di Apple chiamano tutti questo metodo, quindi presumo che serva a uno scopo importante oltre a far scattare il ViewController secondario su un array, ma io non riesco a capire quale sia lo scopo. Che cosa fa questo metodo e perché dovrei chiamarlo?

+3

di Apple [** 2011 ** WWDC] (https://developer.apple.com/videos/wwdc/2011/) la pagina dei video ha una sessione _great_ ("Implementazione del contenimento di UIViewController") su questo argomento. – Alladinian

risposta

81

Mi chiedevo anche questa domanda. Ho guardato i video e il signor Session 102 of the WWDC 2011 View Controller, Bruce D. Nilo, detto questo:

viewWillAppear:, viewDidAppear:, ecc non hanno nulla a che fare con addChildViewController:. Tutto ciò che dice addChildViewController: è "Questo controller di visualizzazione è figlio di quello" e non ha nulla a che fare con l'aspetto della vista. Quando vengono chiamati è associato a quando le visualizzazioni si spostano dentro e fuori dalla gerarchia della finestra.

Quindi sembra che la chiamata a addChildViewController: faccia molto poco. Gli effetti collaterali della chiamata sono la parte importante. Provengono dalle relazioni parentViewController e childViewControllers. Qui ci sono alcuni degli effetti collaterali che ne so:

  • metodi di inoltro Aspetto per Bambini controllori
  • Forwarding metodi di rotazione
  • avvertenze
  • (forse) di memoria inoltro
  • Evitare gerarchie VC incoerenti, soprattutto in transitionFromViewController:toViewController:… dove entrambi i VC devono avere lo stesso genitore
  • Consentire ai controller di visualizzazione contenitore personalizzati di prendere parte a Conservazione e ripristino dello stato
  • T aking parte della catena di risponditore
  • agganciando il navigationController, tabBarController, ecc proprietà
+0

È la sessione 102 non 101 – SeanChense

+0

+1 per la catena di risposta. addChildViewController è richiesto se si desidera ricevere eventi di tocco su una sottoview posseduta da un bambino UIViewController – charlieb

9

-[UIViewController addChildViewController:] aggiunge solo il controller di visualizzazione passato in una matrice di viewControllers a cui un viewController (il genitore) desidera mantenere il riferimento. Dovresti aggiungere tu stesso le visualizzazioni di viewController sullo schermo aggiungendole come subviews di un'altra vista (ad esempio la vista di parentViewController). C'è anche un oggetto di convenienza in Interface Builder per usare childViewControllers in Storyboard.

In precedenza, per mantenere il riferimento di altri viewController di cui si utilizzavano le viste, si doveva conservare il riferimento manuale di essi in @properties. Avere una proprietà build-in come childViewControllers e di conseguenza parentViewController è un modo conveniente per gestire tali interazioni e creare viewController composti come UISplitViewController che trovi nelle app per iPad.

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

Questo è tutto.

+0

Qual è la tua base per pensare che è tutto ciò che fa? Inoltre, puoi fornire un elenco degli "eventi di sistema" che vengono ricevuti dal bambino? Una ricerca su Google per gli eventi di sistema "iOS" "non genera molto; non sembra essere un termine usato da Apple? –

+0

È fondamentalmente un metodo di convenienza che consente di aggiungere la vista del View Controller B come vista secondaria di View Controller A, ma di avere il View Controller B per gestire la sua vista. Affinché questo funzioni correttamente, è necessario assicurarsi che View Controller B riceva gli eventi di sistema (leggere i callback di UIViewControllerDelegate). 'addChildViewController' ti aggancia tutto, per farti risparmiare lo sforzo di passare tutto manualmente. –

94

Penso che un esempio valga più di mille parole.

Stavo lavorando a un'applicazione di libreria e volevo mostrare una bella vista del blocco note che appare quando l'utente vuole aggiungere una nota.

enter image description here

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 NotepadViewController e aggiungo la sua vista di root come sottoview alla vista principale. Fin qui tutto bene.

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

enter image description here

così ho voluto cambiare l'immagine blocco note e spostare in su. E per farlo, ho scritto il codice corretto nel metodo willAnimateRotationToInterfaceOrientation:duration:, ma quando ho eseguito l'app non è successo niente! E dopo il debug, ho notato che nessuno dei metodi di rotazione di UIViewController viene effettivamente chiamato in NotepadViewController. Vengono chiamati solo quei metodi nel controller della vista principale.

Per risolvere questo problema, ho dovuto chiamare manualmente tutti i metodi da NotepadViewController quando sono stati chiamati nel controller della vista principale. Ciò renderà presto le cose complicate e creerà una dipendenza aggiuntiva tra i componenti non collegati nell'app.

Questo era nel passato, prima che venga introdotto il concetto di controller di visualizzazione figlio. Ma ora hai solo bisogno di addChildViewController per il controller di visualizzazione principale e tutto funzionerà come previsto senza alcun altro lavoro manuale.

Edit: Ci sono due categorie di eventi inoltrati ai controller Bambini:

1- Metodi Aspetto:

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

2- metodi di rotazione:

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

È inoltre possibile controllare quali categorie di eventi si desidera inoltrare automaticamente ignorando shouldAutomaticallyForwardRotationMethods e shouldAutomaticallyForwardAppearanceMethods.

+0

Dalla documentazione e dopo aver eseguito un test rapido, non penso ci sia alcun altro evento che viene inoltrato solo se si aggiunge addChildViewController al controller principale. – Hejazi

+0

si desidera inoltrare automaticamente viewWillLayoutSubviews – MobileMon