View controllers
Last updated
Last updated
Los view controllers son la C del MVC. Actúan como el “pegamento” que relaciona la vista con el modelo. El controlador es el lugar típico para poner el código que reacciona a los eventos del usuario. Por ejemplo qué hacer cuando se pulsa un botón.
En cada momento hay un root view controller, que es el “principal”. En los casos más simples, una pantalla de nuestra aplicación tendrá un único view controller. En general, un controlador puede tener a su vez controladores “hijos”, como veremos.
Hay dos tipos básicos de controladores: los que muestran directamente contenido (content controllers) y los que contienen otros contenedores (container controllers).
Cuando se usan storyboards no tenemos que preocuparnos de instanciar el controlador adecuado, el proceso es automático. También el controlador se asocia automáticamente a la vista que hemos definido en el Interface Builder.
Podemos hacerlo de varias formas. De más sencilla a más compleja (pero también más flexible)
Gráficamente, con storyboards: tanto las vistas como el controlador están en el storyboard
Gráficamente, con .nib
: en cada archivo nib
guardamos una pantalla (con su jerarquía de vistas), pero no el controlador, que se crea por código
Totalmente por código: tenemos que instanciar el controlador y en su método loadView
crear la jerarquía de vistas que queremos que contenga (UIView
, UIButton
, lo que sea) y asignar la raíz de la jerarquía de vistas a self.view
.
En los siguientes apartados vamos a ver cada una de estas posibilidades con algo más de detalle.
Desde Xcode 5 los storyboards son la forma recomendada por Apple de crear interfaces de usuario. Un storyboard contiene la representación gráfica de las “pantallas” (los controladores) que componen nuestra aplicación y de las relaciones entre ellas. Además el sistema se encarga automáticamente de moverse por las pantallas cuando sucedan determinados eventos, instanciando los controladores y las vistas automáticamente.
En cada momento habrá un view controller inicial que es el que se muestra cuando se carga la aplicación. Se distingue visualmente porque tiene una flecha apuntando a él desde la izquierda:
También podemos arrastrar la flecha que indica que un controlador es el inicial desde el actual hasta el que queremos convertir en inicial.
Son las transiciones entre los view controllers. Podemos crear un segue visualmente con Ctrl+Arrastrar
entre un elemento cualquiera de un view controller (por ejemplo un botón), que será el de controller de origen, y el controller destino. Se nos dará a elegir el tipo de segue en un menú contextual. Si es un controller de contenido el típico es modal
(o también popover
en iPad). El tipo push
es para controladores de navegación, y el custom
para escribir nuestro propio tipo.
Cuando se va a saltar de un controller a otro a través de un segue, se llama al método prepareForSegue:sender:
del controller origen. Podemos sobreescribir este método para pasarle datos al controller destino. El primer parámetro va a instanciarse al segue y a partir de este podemos obtener una referencia al destino.
Una cosa que no podemos hacer visualmente sin escribir algo de código es volver a la pantalla anterior cuando hemos seguido un segue. Para conseguirlo tenemos que hacer dos cosas:
La primera, implementar en el controller al que se vuelve un método que puede tener el nombre que deseemos pero debe devolver un IBAction
y tener como único parámetro un UIStoryboardSegue *
. Por ejemplo
Ahora en la pantalla del storyboard desde la que volvemos conectamos con Ctrl+Arrastrar
entre el elemento de interfaz que debe producir el unwind, y el icono de Exit
que aparece en la parte de arriba.
Si intentamos hacer esta operación de
Ctrl+Arrastrar
sin haber implementado el método anterior, veremos que no tiene efecto
En el método del unwinding, nótese que podemos usar el parámetro, que es el segue, para obtener el destinationController
, que ahora será el controller al que volvemos. También podemos acceder al controller desde el que volvemos en la propiedad sourceController
.
Finalmente, decir que cuando se produce un unwind, el controlador desde el que se vuelve también recibe una llamada a prepareForSegue:sender
, método que podemos sobreescribir si queremos aprovechar para realizar alguna operación antes de volver.
Hasta que apareció iOS 5 los NIB eran la forma habitual de crear interfaces de usuario, pero por defecto las últimas versiones de Xcode (desde la 5, correspondiéndose con iOS7) usan storyboards. Nótese que un NIB contiene únicamente “una pantalla” de nuestra aplicación y que por tanto es responsabilidad del desarrollador cambiar de un controlador a otro y cargar el NIB correspondiente conforme se va navegando.
Un archivo .xib, que es lo que vemos en la lista de archivos de proyecto en Xcode, es básicamente un NIB serializado en forma de XML, lo que podemos comprobar haciendo clic sobre él con el botón derecho en Xcode y seleccionando Open as
> Source code
.
En Xcode podemos crear un NIB de dos formas:
Crear un controller y automáticamente un NIB asociado
Crear directamente el NIB y luego asociarle manualmente un controller
En la primera de las posibilidades nos iríamos a crear la nueva clase del controller (una cocoa touch class
). Especificaríamos que hereda de UIViewController
y marcaríamos la casilla de Also create XIB File
.
Como el controlador y el NIB están asociados por defecto, para cargar la pantalla con la jerarquía de vistas basta con cargar el controlador:
En el segundo caso (asociación “manual”), cuando inicializamos el controlador debemos especificar qué NIB debe cargar
Vamos a hacer una aplicación que vamos a llamar “Pioneras”, y que nos dará datos de algunas mujeres pioneras de la informática. La aplicación tendrá una pantalla principal en la que aparecerán sus imágenes, y haciendo tap sobre cada una podremos ir a las pantallas secundarias donde se nos dará más información.
En la carpeta sesion1/recursos/
de las plantillas tenemos las imágenes de las tres pioneras: Ada Lovelace, Grace Hopper y Barbara Liskov, que como siempre arrastraremos al images.xcassets
. También tenemos los textos sobre ellas que se mostrarán en las pantallas secundarias.
Crea tres botones en la pantalla principal, y para cada uno de ellos en lugar de texto vamos a usar como imagen de fondo la de cada mujer. Al final cada botón debería ocupar todo el ancho de la pantalla y un tercio del alto.
Arrastra un nuevo “view controller” al storyboard (una “pantalla” nueva), que será el que aparezca cuando se pulse en el primero de los botones (el de Ada Lovelace). Inserta un campo de texto de varias líneas (text view) y copia en él el contenido de sesion1/recursos/lovelace.txt
Ahora establece el segue entre las dos pantallas: haz Ctrl+Arrastrar
desde el primero de los botones con la imagen de Ada Lovelace hasta la segunda pantalla. En el menú contextual elige el tipo modal
, ya que los otros no funcionarán.
Si haces clic en el segue y vas al Attribute inspector puedes cambiar las propiedades, pero tal como está hecha la aplicación solo va a tener efecto la transition
. Pon el valor que quieras, salvo partial curl
que puede dar problemas a la hora de volver atrás en el segue.
Ejecuta el proyecto para comprobar que funciona lo que has hecho, aunque todavía no puede volver atrás desde la pantalla secundaria
Implementa la opción de volver atrás de la secundaria a la principal
Crea un botón “atrás” en la secundaria y colócalo en la parte de arriba (para que no lo tape el teclado on-screen si aparece)
En el controller destino crea un método para que funcione el unwinding (no hace falta que haga nada, solo que exista)
Con Ctrl+Arrastrar
conecta el botón “atrás” con el icono de “Exit” de la parte superior del controller
Ejecuta el proyecto y comprueba que puedes volver atrás desde la pantalla secundaria
Repite lo que has hecho en el caso de Ada Lovelace para las otras dos mujeres, creando las pantallas secundarias y la navegación adelante y atrás.
Es un poco redundante tener tantas pantallas secundarias cuando en realidad lo único que cambia es el texto a mostrar. Valdría con una sola secundaria en la que cambiáramos dinámicamente dicho texto. Vamos a implementarlo así.
Lo primero es guardar una copia del estado actual del proyecto. Hay dos posibilidades:
con git
podemos crear una etiqueta o tag que nos marque la versión actual del proyecto para poder recuperarla luego. No obstante Xcode no nos permite gestionar tags, por lo que, tras asegurarnos de que hemos hecho commit y push de todos los cambios actuales, haríamos desde la terminal (estando dentro del repositorio):
Otra posibilidad es crear una copia manual de los ficheros del proyecto y guardarlo en una carpeta v1.0
de las plantillas
Ahora podéis eliminar los segues y las pantallas secundarias, es mejor crearlos de nuevo.
Crea de nuevo una pantalla secundaria con un campo de texto de varias líneas
Con Ctrl+arrastrar
podemos crear un segue desde cada uno de los botones hasta la pantalla. Habrán tres segues que lleguen a la misma, no debería ser problema.
Añádele a la pantalla el botón de “atrás” y conéctalo con el icono de “exit”. El código necesario para el unwinding (método retornoDeSecundaria
) ya debería estar en el ViewController.m
Comprueba que la navegación funciona correctamente yendo adelante y atrás
Si en la parte derecha de la pantalla miras el identity inspector verás que el controlador de la pantalla secundaria es un tipo propio de Cocoa, el UIViewController
. Vamos a cambiarlo por uno propio
Crea una nueva clase de Cocoa Touch, (File> New > File…, plantilla “cocoa touch class”). En la segunda pantalla del asistente dale a la clase el nombre SecundarioViewController
y haz que sea una subclase de UIViewController
. Deja sin marcar la opción de crear el .XIB
En el storyboard, selecciona el controller de la pantalla secundaria (es mejor que lo hagas pulsando en el primero de los iconos que aparecen en la parte superior)
Una vez seleccionado, ve al identity inspector en el área de Utilities
y en el apartado de Custom class
selecciona como clase la que has creado, SecundarioViewController
Tienes que añadir un outlet al campo de texto para que su contenido se pueda cambiar desde el controlador secundario. Hazlo como habitualmente, con ctrl+arrastrar entre el campo y el SecundarioViewController.h
, en el assistant editor
.
Lo primero es añadir físicamente los ficheros *.txt
con los textos al proyecto para que se puedan cargar dinámicamente por código. Pulsa con el botón derecho sobre el proyecto y selecciona Add files to Pioneras
. Selecciona los tres .txt
, que se añadirán al proyecto
Para que le podamos decir al controlador secundario qué fichero tiene que abrir, vamos a crear una @property
en el SecundarioViewController
llamada nomFich
de tipo NSString*
//En el SecundarioViewController.h @property NSString *nomFich;
Para establecer una asociación sencilla entre cada segue y los datos a mostrar puedes usar el identificador del segue. Haz clic sobre él y en el Attributes inspector
cambia su identifier
, respectivamente por lovelace
, hopper
y liskov
ahora en la clase ViewController
, que es el controlador de la pantalla principal, puedes implementar el prepareForSegue:sender
Finalmente, en el viewDidLoad
del SecundarioViewController
puedes acceder a la propiedad self.nomFich
, cargar el texto del fichero y mostrarlo en el campo de texto.
Para convertir un view controller en inicial, teniéndolo seleccionado ir al icono de propiedades del área de Utilities
y marcar sobre el checkbox Is initial view controller
Podemos configurar las propiedades del segue haciendo clic sobre él y yendo al icono de propiedades del área de Utilities
. Aquí podemos cambiar el tipo y también la transición usada para navegar de una pantalla a otra.
Un archivo NIB (o .xib
, en un momento veremos la diferencia) contiene la jerarquía de vistas asociada a un determinado view controller, pero normalmente no se crea de manera manual, sino visualmente con el Interface Builder. De hecho, el nombre significa “NeXT Interface Builder”, referenciando la famosa plataforma de la que hereda y es deudora Cocoa.