¿Qué hace addChildViewController en realidad?

Me estoy sumergiendo por primera vez en el desarrollo de iOS, y una de las primeras cosas que he tenido que hacer es implementar un controlador de vista de contenedor personalizado , vamos a llamarlo SideBarViewController , que intercambia cuál de los posibles controladores de vista secundarios se muestra, casi exactamente como un controlador de barra de tabs estándar. (Es más o menos un Controlador de barra de tabs, pero con un menú lateral oculta en lugar de una barra de tabs).

De acuerdo con las instrucciones de la documentación de Apple, llamo addChildViewController cada vez que agrego un ViewController secundario a mi contenedor. Mi código para cambiar el controlador de vista hijo actual que se muestra por el SideBarViewController ve así:

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

Entonces comencé a tratar de averiguar qué hace addChildViewController aquí, y me di cuenta de que no tengo ni idea. Además de pegar el nuevo ViewController en la matriz .childViewControllers , parece que no tiene efecto en nada. Las acciones y salidas desde la vista del controlador del niño al controlador del niño que he establecido en el guión gráfico todavía funcionan bien, incluso si nunca llamo addChildViewController , y no puedo imaginar qué otra cosa podría afectar.

De hecho, si reescribo mi código para no llamar a addChildViewController , y en su lugar se ve así …

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

… entonces mi aplicación todavía funciona perfectamente, por lo que puedo ver!

La documentación de Apple no arroja mucha luz sobre lo que addChildViewController hace, o por qué se supone que debemos llamarlo. El scope completo de la descripción relevante de lo que hace el método o por qué debería usarse en su sección en la Referencia de clase UIViewController es, en este momento:

Agrega el controlador de vista dado como un niño. … Este método solo debe ser llamado por una implementación de un controlador de vista de contenedor personalizado. Si anula este método, debe llamar a super en su implementación.

También hay este párrafo anterior en la misma página:

El controlador de vista de contenedor debe asociar un controlador de vista secundaria consigo mismo antes de agregar la vista de raíz del elemento secundario a la jerarquía de vista. Esto permite que iOS enrute los eventos a los controladores de vista secundarios y a las vistas que administran esos controladores. Del mismo modo, después de que elimina la vista de raíz de un niño de su jerarquía de vista, debe desconectar ese controlador de vista secundaria de sí mismo. Para crear o deshacer estas asociaciones, su contenedor llama a métodos específicos definidos por la clase base. Estos métodos no están destinados a ser llamados por clientes de su clase de contenedor; solo deben ser utilizados por la implementación de su contenedor para proporcionar el comportamiento de contención esperado.

Estos son los métodos esenciales que podría necesitar para llamar:

addChildViewController:
removeFromParentViewController
willMoveToParentViewController:
didMoveToParentViewController:

pero no ofrece ninguna pista sobre los “eventos” o “comportamientos de contención esperados” de los que está hablando, o por qué (o incluso cuando) llamar a estos métodos es “esencial”.

Los ejemplos de controladores de vista de contenedores personalizados en la sección “Controladores de vista de contenedor personalizado” de la documentación de Apple llaman a este método, así que supongo que sirve para un propósito importante más allá de simplemente mostrar el controlador ViewControl en una matriz, pero no puedo entender cuál es ese propósito ¿Qué hace este método y por qué debería llamarlo?

Me preguntaba sobre esta pregunta también. Vi la Sesión 102 de los videos de WWDC 2011 y el controlador de Mr. View, Bruce D. Nilo , dijeron esto:

viewWillAppear: viewDidAppear: etc. no tienen nada que ver con addChildViewController: Todo lo que addChildViewController: hace es decir “Este controlador de vista es hijo de ese” y no tiene nada que ver con la apariencia de la vista. Cuando se llaman se asocia con cuando las vistas se mueven dentro y fuera de la jerarquía de la ventana.

Entonces parece que la llamada a addChildViewController: hace muy poco. Los efectos secundarios de la llamada son la parte importante. Vienen de las relaciones parentViewController y childViewControllers . Estos son algunos de los efectos secundarios que yo sé:

  • Reenvío de métodos de apariencia a los controladores de vista infantil
  • Forwarding rotation methods
  • (Posiblemente) reenviar las advertencias de memoria
  • Evitar jerarquías de VC inconsistentes, especialmente en transitionFromViewController:toViewController:… donde ambos VCs necesitan tener el mismo padre
  • Permitir que los controladores de vista de contenedor personalizados participen en la conservación y restauración del estado
  • Participando en la cadena de respuesta
  • tabBarController propiedades de navigationController , tabBarController , etc.

Creo que un ejemplo vale más que mil palabras.

Estaba trabajando en una aplicación de la biblioteca y quería mostrar una bonita vista de bloc de notas que aparece cuando el usuario quiere agregar una nota.

enter image description here

Después de probar algunas soluciones, terminé inventando mi propia solución personalizada para mostrar el bloc de notas. Entonces, cuando quiero mostrar el bloc de notas, creo una nueva instancia de NotepadViewController y agrego su vista raíz como una subvista a la vista principal. Hasta aquí todo bien.

Luego noté que la imagen del bloc de notas está parcialmente oculta debajo del teclado en modo horizontal.

enter image description here

Así que quería cambiar la imagen del bloc de notas y cambiarla. Y para hacerlo, escribí el código correcto en willAnimateRotationToInterfaceOrientation:duration: method, pero cuando ejecuté la aplicación no sucedió nada. Y después de la depuración noté que ninguno de los métodos de rotación de UIViewController se llama realmente en NotepadViewController . Solo se están llamando a los métodos en el controlador de vista principal.

Para resolver esto, tuve que llamar todos los métodos desde NotepadViewController de NotepadViewController manual cuando se llaman en el controlador de vista principal. Esto pronto complicará las cosas y creará una dependencia adicional entre los componentes no relacionados en la aplicación.

Eso fue en el pasado, antes de que se introdujera el concepto de controladores de vista infantil. Pero ahora, solo necesita addChildViewController el control de vista de addChildViewController al controlador de vista principal y todo funcionará como se espera sin más trabajo manual.

Editar: hay dos categorías de eventos que se envían a los controladores de vista secundarios:

1- Métodos de apariencia:

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

2- Métodos de rotación:

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

También puede controlar qué categorías de eventos quiere que se reenvíen automáticamente al anular los shouldAutomaticallyForwardRotationMethods y shouldAutomaticallyForwardAppearanceMethods .

-[UIViewController addChildViewController:] solo agrega el controlador pass in view en una matriz de viewControllers a los que un viewController (el principal) quiere mantener como referencia. En realidad, debe agregar las vistas de viewController en la pantalla, agregándolas como subvistas de otra vista (por ejemplo, la vista de parentViewController). También hay un objeto de conveniencia en Interface Builder para usar childrenViewControllers en Storyboards.

Anteriormente, para mantener la referencia de otros viewControllers de los que utilizaba las vistas, tenía que mantener una referencia manual de ellos en @properties. Tener una propiedad childViewControllers como childViewControllers y consecuentemente parentViewController es una manera conveniente de administrar tales interacciones y construir viewControllers compuestos como el UISplitViewController que se encuentra en las aplicaciones de iPad.

Además, los niñosViewControllers también reciben automáticamente todos los eventos del sistema que recibe el padre: -viewWillAppear, -viewWillDisappear, etc. Anteriormente, debería haber llamado a este método manualmente en su “childrenViewControllers”.

Eso es.