¿Cómo agregar una subvista que tiene su propio UIViewController en Objective-C?

Estoy luchando con las subvistas que tienen sus propios UIViewControllers . Tengo un UIViewController con una vista (rosa claro) y dos botones en una toolbar . Quiero que se muestre la vista azul cuando se presiona el primer botón y se presiona la vista amarilla para mostrar con el segundo botón. Debería ser fácil si solo quisiera mostrar una vista. Pero la vista azul contendrá una tabla, por lo que necesita su propio controlador. Esa fue mi primera lección. Empecé con esta pregunta SO, donde me enteré de que necesitaba un controlador para la mesa.

Por lo tanto, voy a hacer una copia de seguridad y dar unos pequeños pasos aquí. A continuación se muestra una imagen de un punto de partida simple con mi Utility ViewController (el controlador de vista principal) y los otros dos controladores (azul y amarillo). Imagine que cuando se muestra por primera vez Utility ViewController (la vista principal), se mostrará la vista azul (predeterminada) donde se encuentra la vista rosa. Los usuarios podrán hacer clic en los dos botones para ir y volver y la vista rosada NUNCA se mostrará. Solo quiero que la vista azul vaya donde está la vista rosada y la vista amarilla que va donde está la vista rosada. Espero que esto tenga sentido.

Imagen Simple Storyboard

Estoy tratando de usar addChildViewController . Por lo que he visto, hay dos formas de hacer esto: la Vista de Contenedor en el storyboard o addChildViewController progtwigción. Quiero hacerlo programáticamente. No quiero usar un NavigationController o una barra de tabs. Solo quiero agregar los controladores e introducir la vista correcta en la vista rosa cuando se presiona el botón asociado.

A continuación está el código que tengo hasta ahora. Todo lo que quiero hacer es mostrar la vista azul donde está la vista rosa. Por lo que he visto, debería ser capaz de agregar a ChildrenViewController y addSubView. Este código no me está haciendo eso. Mi confusión se está poniendo mejor de mí. ¿Alguien puede ayudarme a mostrar la vista azul donde está la vista rosada?

Este código no tiene la intención de hacer nada más que mostrar la vista azul en viewDidLoad.

IDUtilityViewController.h

 #import  @interface IDUtilityViewController : UIViewController @property (strong, nonatomic) IBOutlet UIView *utilityView; @end 

IDUtilityViewController.m

 #import "IDUtilityViewController.h" #import "IDAboutViewController.h" @interface IDUtilityViewController () @property (nonatomic, strong) IDAboutViewController *aboutVC; @end @implementation IDUtilityViewController - (void)viewDidLoad { [super viewDidLoad]; self.aboutVC = [[IDAboutViewController alloc]initWithNibName:@"AboutVC" bundle:nil]; [self addChildViewController:self.aboutVC]; [self.aboutVC didMoveToParentViewController:self]; [self.utilityView addSubview:self.aboutVC.aboutView]; } @end 

————————–EDITAR———————– ——-

El self.aboutVC.aboutView es nil. Pero lo conecté en el storyboard . ¿Todavía necesito crear una instancia?

enter image description here

Esta publicación, que data de los primeros días del iOS moderno, generalmente tiene la syntax más reciente, como Swift 4 actualmente. Si estás comenzando con iOS, autolayout, etc. te pondrá en marcha.

En iOS hoy “todo es una vista de contenedor” . Es la forma básica de hacer aplicaciones hoy.

Una aplicación puede ser tan simple que solo tiene una vista. Pero incluso en ese caso, cada “cosa” en la pantalla es una vista de contenedor.

Es así de fácil …


(A) Arrastre una vista de contenedor a su escena …

Arrastra una vista de contenedor a tu vista de escena. (Del mismo modo que arrastraría, digamos, un UIButton).

La vista de contenedor es lo marrón en esta imagen. En realidad está dentro de tu vista de escena.

enter image description here

Cuando arrastra una vista de contenedor a su vista de escena, Xcode le da automáticamente dos cosas :

  1. Obtienes la vista de contenedor dentro de la vista de escena , y

  2. obtienes un nuevo UIViewController que simplemente está sentado en algún lugar sobre el blanco de tu guión gráfico .

Los dos están conectados con la cosa del “Símbolo masónico”, ¡que se explica a continuación!


(B) Haz clic en ese nuevo controlador de vista (lo nuevo que Xcode creó para ti en algún lugar del área blanca, no en lo que está dentro de tu escena ) … ¡y cambia la clase!

Es realmente así de simple.

Ya terminaste


Aquí está lo mismo explicado visualmente.

Observe la vista de contenedor en (A) .

Observe el controlador en (B) .

muestra una vista de contenedor y el controlador de vista asociado

Haga clic en B. (Eso es B – no A!)

Ve al inspector en la parte superior derecha. Observe que dice “UIViewController”

[ enter image description here ] [3]

Cámbielo a su propia clase personalizada, que es un UIViewController.

enter image description here

Entonces, tengo un Snap clase Swift que es un UIViewController .

enter image description here

Entonces, donde dice “UIViewController” en el Inspector, escribí “Ajustar”.

(Como de costumbre, Xcode completará automáticamente “Ajustar” cuando empiece a escribir “Ajustar …”).

Eso es todo, hay que terminar.


Cómo cambiar la vista de contenedor, por ejemplo, a una vista de tabla.

Entonces, cuando hace clic para agregar una vista de contenedor, Apple le da automáticamente un controlador de vista vinculado, sentado en el guión gráfico.

Como sucede (2017): lo convierte en un UIViewController por defecto.

Eso es una tontería: debería preguntar qué tipo necesitas. Por ejemplo, a menudo necesita una vista de tabla. He aquí cómo cambiarlo a algo diferente:

En el momento de escribir, Xcode le da un UIViewController por defecto. Supongamos que quiere un UICollectionViewController en UICollectionViewController lugar:

(i) Arrastre una vista de contenedor a su escena. Mire el UIViewController en el guión gráfico que Xcode le brinda por defecto.

(ii) Arrastre un nuevo UICollectionViewController a cualquier parte del área blanca principal del guión gráfico.

(iii) Haga clic en la vista de contenedor dentro de su escena. Haga clic en el inspector de conexiones. Tenga en cuenta que hay un “Segue desencadenado”. Pase el mouse sobre la “Segue desencadenada” y observe que Xcode resalta todo el UIViewController no deseado.

(iv) Haga clic en la “x” para eliminar realmente ese Triggered Segue.

(v) Arrastre desde ese Triggered Segue (viewDidLoad es la única opción). Arrastre por el guión gráfico hacia su nuevo UICollectionViewController. Suelta y aparece una ventana emergente. Debe seleccionar incrustar .

(vi) Simplemente elimine todo el UIViewController no deseado. Ya terminaste

Versión corta: elimine el UIViewController no deseado. Coloque un nuevo UICollectionViewController en el guión gráfico. Mantenga presionada la tecla Control: las conexiones de la vista del contenedor – Trigger Segue – viewDidLoad, a su nuevo controlador. Asegúrese de seleccionar “incrustar” en la ventana emergente.


Ingresando el identificador de texto …

Tendrá uno de estos símbolos masónicos “cuadrados en un cuadrado” : está en la “línea curva” que conecta su vista de contenedor con el controlador de vista.

El “símbolo masónico” es la transición.

enter image description here

Seleccione el segue haciendo clic en el símbolo “símbolo masónico”.

Mira a tu derecha.

DEBE escribir un identificador de texto para el segue.

Tú decides el nombre. Puede ser cualquier cadena de texto. Una opción sensata es a menudo “segueClassName”.

Si sigue ese patrón, todos sus segmentos se denominarán segueClockView, seguePersonSelector, segueSnap, segueCards, etc.

Luego, ¿dónde usas ese identificador de texto?


Cómo conectarse ‘al’ controlador infantil …

Luego, haga lo siguiente, en código, en ViewController de toda la escena.

Supongamos que tiene tres vistas de contenedor en la escena. Cada vista de contenedor tiene un controlador diferente, digamos “Ajustar”, “Reloj” y “Otro”.

Última syntax de Swift3 (2017)

 var snap:Snap? var clock:Clock? var other:Other? override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if (segue.identifier == "segueSnap") { snap = (segue.destination as! Snap) } if (segue.identifier == "segueClock") { clock = (segue.destination as! Clock) } if (segue.identifier == "segueOther") { other = (segue.destination as! Other) } } 

Es así de simple. Usted conecta una variable para referirse a los controladores, usando la llamada prepareForSegue .


Cómo conectarse en la ‘otra dirección’, hasta el padre …

Supongamos que está “en” el controlador que ha puesto en una vista de contenedor (“Ajustar” en el ejemplo).

Puede ser confuso llegar al controlador de vista “jefe” que está sobre usted (“Tablero” en el ejemplo). Afortunadamente, es así de simple:

 // Dash is the overall scene. // Here we are in Snap. Snap is one of the container views inside Dash. class Snap { var myBoss:Dash? override func viewDidAppear(_ animated: Bool) { // MUST be viewDidAppear super.viewDidAppear(animated) myBoss = self.parent as? Dash } 

Crítico: solo funciona desde viewDidAppear o posterior. No funcionará en viewDidLoad .

Ya terminaste


Importante: eso solo funciona para vistas de contenedores.

Consejo avanzado importante: no olvide que solo funciona para vistas de contenedores.

Hoy en día con los identificadores del guión gráfico, es común hacer aparecer nuevas vistas en la pantalla (más bien como en el desarrollo de Android). Entonces, digamos que el usuario quiere editar algo …

  // let's just pop a view on the screen. // this has nothing to do with container views // let e = ...instantiateViewController(withIdentifier: "Edit") as! Edit e.modalPresentationStyle = .overCurrentContext self.present(e, animated: false, completion: nil) 

Al usar una vista de contenedor, SE GARANTIZA que Dash será el controlador de vista principal de Snap.

Sin embargo, NO ES NECESARIAMENTE EL CASO cuando utiliza instantiateViewController.

Muy confusamente, en iOS, el controlador de vista padre no está relacionado con la clase que lo creó. ( Puede ser lo mismo, pero generalmente no es lo mismo.) El patrón self.parent es solo para vistas de contenedor.

(Para obtener un resultado similar en el patrón instanciadoViewController, debe usar un protocolo y un delegado, recordando que el delegado será un enlace débil).


prepareForSegue mal nombrado …

¡Vale la pena señalar que “prepareForSegue” es un nombre realmente malo!

“prepareForSegue” se utiliza para dos propósitos: cargar vistas de contenedor y, segmentar entre escenas.

Pero en la práctica, muy rara vez se segue entre escenas. Mientras que casi todas las aplicaciones tienen muchas, muchas, vistas de contenedores como una cuestión de rutina.

Tendría mucho más sentido si “prepareForSegue” se llamara algo así como “loadingContainerView”.


Más de uno…

Una situación común es: tiene un área pequeña en la pantalla, donde desea mostrar uno de varios controladores de vista diferentes. Por ejemplo, uno de los cuatro widgets.

La forma más simple de hacer esto: solo tiene cuatro vistas de contenedores diferentes, todas sentadas en la misma área idéntica . En tu código, simplemente oculta los cuatro y activa el que quieras que sea visible.

En el guión gráfico, tenga un “titular” vacío UIView, que simplemente contiene las cuatro vistas del contenedor. Luego puede dimensionar o mover los cuatro a la vez al dimensionar o mover el “soporte”. En su código, simplemente tiene cuatro UIView venta UIView , uno para cada una de las vistas del contenedor. Copie y pegue el código de arriba, “Cómo conectar con el controlador para niños”, para conectar los cuatro controles de vista contenidos.


Nota: ¡llegan las referencias del guión gráfico!

Como señala SimplGy a continuación: “Las referencias de Storyboard de iOS 9 hacen que las vistas de contenedor sean aún más impresionantes. Puedes definir tu vista reutilizable (controlador) donde quieras y referenciarla desde cualquier vista de contenedor en múltiples guiones gráficos modulares”.

También tenga en cuenta que, de manera bastante confusa, a menudo hoy en día no se molesta con las vistas de contenedores.

Simplemente instantiateViewController#withIdentifier en muchas situaciones.

Pero observe el “gotchya” sobre .parent explicado arriba. El punto central de las vistas de contenedores es que se asegura de forma instantánea y simple a la cadena matriz.

Si utiliza instantiateViewController#withIdentifier utilizando una referencia de guión gráfico, debe instantiateViewController#withIdentifier un protocolo y un delegado (teniendo en cuenta que el delegado será un enlace débil). Pero luego puedes usarlo “sobre la marcha” de manera flexible en cualquier lugar.

Por el contrario, utilizar una vista de contenedor “fija”, por así decirlo, es extremadamente simple, y al instante se conecta entre padres e hijos como se explicó anteriormente.

Veo dos problemas. En primer lugar, dado que está creando los controladores en el guión gráfico, debe crear instancias de ellos con instantiateViewControllerWithIdentifier: no initWithNibName:bundle: En segundo lugar, cuando agrega la vista como una subvista, debe darle un marco. Asi que,

 - (void)viewDidLoad { [super viewDidLoad]; self.aboutVC = [self.storyboard instantiateViewControllerWithIdentifier:@"aboutVC"]; // make sure you give the controller this same identifier in the storyboard [self addChildViewController:self.aboutVC]; [self.aboutVC didMoveToParentViewController:self]; self.aboutVC.view.frame = self.utilityView.bounds; [self.utilityView addSubview:self.aboutVC.aboutView]; }