Desarrollo iOS: Primeros pasos con Storyboard
En este artículo vamos a ver paso por paso como utilizar la funcionalidad Storyboarding de iOS 5, que nos facilitará enormemente la gestión de múltiples pantallas en nuestra App.
En primer lugar vamos a tratar de explicar cual es la principal diferente entre la forma de trabajar antigua y la forma de trabajar nueva.
Hasta ahora, si teníamos una aplicación que tenía un total de 3 pantallas, en lo referente a la capa de vista, debíamos tener 3 archivos de Interface Builder, donde se elaboraba visualmente la estructura y elementos de cada pantalla. Para poder establecer navegación entre una vista y otra, la única manera era mediante código, con funciones como la que sigue:
[self.navigationController pushViewController:myViewController animated:YES];
Al encontrarse la lógica de navegación en el código, no había una forma rápida de entender la misma, sino que era necesario ir viendo cada ViewController y averiguar qué botón llama a qué pantalla, etc.
Ahora, con Storyboarding, esos múltiples ficheros de Interface Builder, se sustituyen por un único fichero .storyboard. Este fichero se edita desde el propio Xcode, al haberse “eliminado” Interface Builder con la salida de Xcode 4.
En este storyboard no solo podemos ver gráficamente cada una de las vistas de nuestra aplicación, sino que podemos conocer cual es la navegación que se establece entre cada una de ellas.
Esto quizás al principio pueda resultar un poco extraño, pero os puedo asegurar que con el ejemplo que vamos a ver al final de este artículo vais a poder comprobar la potencia de esta nueva forma de trabajar.
Escena: una escena es lo que podríamos denominar una vista de nuestra aplicación, es decir, cada pantalla.
Segue (pronunciado Seg-way): es la navegación/transición entre una escena y otra.
En Xcode sin una sola letra de código vamos a poder establecer cual es la navegación para cada uno de los elementos de la interfaz, e incluso personalizar la forma en que se produce la transición entre los mismos (con una serie de efectos que vienen predefinidos y pudiendo establecer los nuestros personalizados).
Ejemplo práctico
Vamos a hacer un ejemplo de aplicación, donde tengamos un TabBarController inicial con dos pestañas. En la primera de ellas tendremos un sencillo botón que abre una ventana modal en la cual hay un botón que nos permite volver a cerrarla enviándole cierta información. En la segunda pestaña tendremos un TableViewController con su correspondiente pantalla de detalle.
En primer lugar abriremos Xcode y crearemos un nuevo proyecto de tipo “Single View Application”.

A continuación cumplimentaremos la información del proyecto, asegurándonos de chequear en la parte inferior las opciones ARC y Storyboarding.

Una vez se haya creado el proyecto, veremos en nuestra vista “Project Navigator” que la plantilla ha creado automáticamente un fichero con el nombre “MainStoryboard.storyboard”. Pulsando sobre el fichero veremos que nos abre la funcionalidad de Storyboarding, que ahora mismo únicamente cuenta con un ViewController.

Dado que necesitamos tener en primer lugar un TabBarController en nuestra aplicación, podemos añadirlo manualmente desde el Panel de Objetos, pero existe una forma aun más comoda de hacerlo.

Si teniendo seleccionado nuestro ViewController vamos al menú “Editor / Embed in / Tab Bar Controller” será Xcode quien haga todos los pasos por nosotros.

Ahora ya tenemos el TabBarController pero aún nos falta una segunda pestaña que como decíamos en el enunciado deberá ser de tipo TableViewController con un NavigationController que nos permita posteriormente incluir una pantalla de detalle.
Desde el panel de objetos vamos a añadir un nuevo TableViewController.

Una vez añadido, lo seleccionaremos, y pulsaremos el menú “Editor / Embed in / Navigation Controller”, y obtendremos un resultado como el que sigue.

Como se puede observar, el Navigation Controller esta completamente desvinculado del Tab Bar Controller. Para incluirlo como una nueva pestaña deberemos hacer Ctrl clic en el Tab Bar Controller y arrastrar y soltar en el Navigation Controller como cuando creamos un Outlet.
Al soltar nos saldrá una ventaja emergente con las siguientes opciones:

Deberemos seleccionar la opción “Relationship – ViewControllers”. Una vez hecho esto veremos que se ha incluido una nueva conexión entre ambos, y que en el Tab Bar Controller figuran ya dos pestañas.

Dado que en nuestro ejemplo no vamos a tener un DataSource para el TableView, vamos a hacer uso de una nueva funcionalidad de iOS 5 y Xcode 4, que son las Static Cells.
En un próximo artículo hablaremos en mas detalle de esta funcionalidad y de la de prototipado de Celdas desde Storyboarding.
Ahora solo haremos lo mínimo necesario para completar nuestra navegación.
Seleccionando el Table View, nos iremos al Attributes Inspector y seleccionaremos la opción Static Cells.

Con ello veremos que Xcode nos crea varias celdas en nuestro Table View. Eliminaremos todas, dejando solo la primera.
Teniendo seleccionada la celda que nos ha quedado en el table View, nos vamos de nuevo al Attributes Inspector y seleccionamos el tipo Basic.


Ahora vamos a añadir por último un ViewController destinado a la pantalla de detalle del Table View Controller.
Desde el panel de objetos añadimos un View Controller y al igual que hicimos hace un momento, arrastramos desde la celda del TableViewController hasta el ViewController creado. Esta vez seleccionaremos la opción Push que nos permitirá dar uso al NavigationController en el que nos encontramos en ese punto.
Ahora mismo ya tenemos todas las escenas que necesita nuestra aplicación, y como habéis podido observar, hemos establecido su navegación sin una sola letra de código.
Si ejecutamos en este punto la aplicación veremos que ya tenemos las pestañas, tenemos el TableView e incluso pulsando sobre la celda que hemos creado como estática, accedemos a la pantalla de detalle.
Como habréis podido observar, la potencia de Storyboarding es algo excepcional.
Por último vamos a ver como se implementaría el último de nuestros requisitos para este ejemplo: hacer que en la primera pestaña haya un botón que abra una ventana modal, en la cual haya otro botón que cierra dicha ventana devolviendo ciertos datos.
Dado que para hacer esta operativa si que vamos a necesitar algo de código a implementar, vamos a crearnos una clase ViewController para nuestra primera pestaña.
Para ello, añadimos una nueva clase a la que llamaremos PrimerViewController que deberá extender a UIViewController.

Una vez creada la clase, seleccionamos dentro del Storyboard el ViewController correspondiente a la primera pestaña (el que no es un Navigation Controller) y en la pestaña de Identity Inspector seleccionamos como Class PrimerViewController.

A continuación añadimos a nuestro Storyboard un nuevo ViewController desde el panel de objetos. Seguidamente creamos una nueva clase en nuestro proyecto a la que llamaremos SegundoViewController, y tal y como hicimos en la pestaña Identity Inspector de este nuevo View Controller seleccionamos como Class SegundoViewController.

Ahora añadiremos un UILabel y un UIButton al Primer View Controller y hacemos Ctrl clic hasta el SegundoViewController donde soltamos y seleccionamos Modal.
En SegundoViewController creamos un UITextField y un UIButton.

En este punto quizás podríais pensar que para poder ir al PrimerViewController al pulsar el botón Seleccionar bastaría con crear un nuevo Segue. La verdad es que no es posible hacer eso, dado que lo que ocurriría sería que nos crearía una nueva instancia de PrimerViewController, y podríamos hacer que el usuario entrara en un bucle en el cual se crean ViewControllers pero nunca se destruyen.
¿Y entonces como hago para enviar información desde la segunda ventana a la primera y cerrar la ventana modal?
Pues bien, vamos a necesitar crear un protocolo en el SegundoViewController, y nuestro PrimerViewController hará de Delegate e implementará dicho protocolo.
Para ello el código quedaría de la siguiente manera:
SegundoViewController.h
#import <UIKit/UIKit.h> @class SegundoViewController; @protocol SegundoViewControllerDelegate <NSObject> - (void)devolverValor:(NSString *)texto; @end @interface SegundoViewController : UIViewController @property (strong, nonatomic) IBOutlet UITextField *cajaTexto; @property (nonatomic, weak) id <SegundoViewControllerDelegate> delegate; - (IBAction)volver:(id)sender; @end
SegundoViewController.m
#import "SegundoViewController.h"
@implementation SegundoViewController
@synthesize cajaTexto;
@synthesize delegate;
- (IBAction)volver:(id)sender {
[delegate devolverValor:self.cajaTexto.text];
}
- (void)viewDidUnload {
[self setCajaTexto:nil];
[super viewDidUnload];
}
@end
PrimerViewController.h
#import <UIKit/UIKit.h>
#import "SegundoViewController.h"
@interface PrimerViewController : UIViewController <SegundoViewControllerDelegate> {
}
@property (strong, nonatomic) IBOutlet UILabel *etiqueta;
@end
PrimerViewController.m
#import "PrimerViewController.h"
@implementation PrimerViewController
@synthesize etiqueta;
- (void)devolverValor:(NSString *)texto {
self.etiqueta.text = texto;
[self dismissModalViewControllerAnimated:YES];
}
- (void)viewDidUnload {
[self setEtiqueta:nil];
[super viewDidUnload];
}
@end
Ahora bien, ya tenemos todo preparado, pero nos falta un pequeño detalle: indicar a SegundoViewController que su delegate es PrimerViewController.
Y aquí es donde vamos a ver una pequeña pega, o elemento a mejorar en Storyboarding, y es que para poder definir esta relación, debemos obligatoriamente hacer uso de código.
Concretamente vamos tener que asignarnos como delegate en el momento en que se construye el SegundoViewController antes de mostrarse.
Para ello, existe un método denominado prepareForSegue, que nos permite justamente eso. Preparar el nuevo ViewController antes de mostrarlo, ya sea para incializar valores, cargar en el información procedente del ViewController en que nos encontramos, o como es el caso, para definirnos como delegate del mismo.
Para ello incluiremos en nuestro PrimerViewController.m el siguiente método:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
SegundoViewController * segundoViewController = segue.destinationViewController;
segundoViewController.delegate = self;
}
Como se puede ver el método cuenta con un parámetro segue de tipo UIStoryboardSegue de donde podemos entre otras cosas obtener el ViewController al que vamos a ir al ejecutar esta navegación.
En algunos casos, en los que un ViewController tenga navegación a través de botones por ejemplo a varios lugares, preguntar cual es el Segue concreto que se esta ejecutando.
Para ello deberemos identificarlos previamente en el Attributes Inspector.

Esto lo haremos situándonos sobre el icono correspondiente al Segue que queremos identificar.

Una vez hecho esto, dentro de nuestro método prepareForSegue podremos preguntar por el identificador del Segue que se esta ejecutando, y poder así establecer diferentes salidas para cada uno de ellos:
if ([segue.identifier isEqualToString:@"PantallaDetalle"]){
}
Pues esto ha sido todo, y espero que este artículo os sirva para quitaros el miedo con esta potente herramienta que es el Storyboarding.
Una vez se prueba a trabajar así, ya no hay vuelta atrás.
Espero vuestros comentarios.
Gracias por vuestros comentarios privados a través de Twitter. La verdad es que dan ganas de hacer más y más artículos.
Le resultó de ayuda?
Me gustaNo me gustaThis solution has been deemed correct by the post author
Estupendo articulo, no conocía la parte del delegate y el protocol, no me ha hecho falta usarlo todavía, pero esta genial tener unas nociones con ejemplos sencillos para luego ampliarlas.
Gracias por el artículo. Un Saludo.
Le resultó de ayuda?
Me gustaNo me gustaMuchas gracias Ricardo! Cualquier duda, ya sabes donde estamos! ;·)
Le resultó de ayuda?
Me gustaNo me gustaTe felicito por tu blog y te animo a que sigas. Estoy empezando en esto y no veo que haya demasiada información en español o igual es que no la he buscado bien
Estuve mirando por encima esto del storyboard hace unos días y no me acabó de gustar precisamente porque no sabía que existía el método prepareForSegue.
Saludos!
Le resultó de ayuda?
Me gustaNo me gustaMiguel gracias por compartir tus conocimientos. Por fin pude entender el patrón delegate, pero como puedo hacer si necesito compartir datos a una vista que aun no se ha mostrado?
Le resultó de ayuda?
Me gustaNo me gustaHola Gustavo,
Entiendo que te refieres a enviar información a una vista que se va a cargar, pero aún no se ha cargado. Esto lo harías dentro del método prepareForSegue. Instanciamos el destinationViewController, y seteamos las propiedades que queramos enviar al mismo.
Un abrazo.
Le resultó de ayuda?
Me gustaNo me gustaMuchas gracias por este artículo, me ha sido muy útil. Acabo de empezar a estudiar el desarrollo de Apps para iPhone y ha sido motivador encontrarme con este minitutorial. Sin embargo he quedado con un par de dudas sobre el final de la publicación. ¿Será posible escribirte un correo y canalizar por ahí mi inquietud?
Gracias nuevamente.
Le resultó de ayuda?
Me gustaNo me gustaHola no veo que hay que hacer en el boton Seleccionar para volver, lo he hecho todo paso a paso y no me funciona, no consigo volver.
Le resultó de ayuda?
Me gustaNo me gustaBuenas he visto que en el tutorial tocas algo de Table View y te quiero hacer una consulta relacionada con este elemento. Soy bastante novato, te cuento por si me puedes ayudar.
Tengo en el storyboard entre otras cosas un Table View Controller con 10 celdas. Las he creado de forma Static Cells.
De una de estas celdas pulsando Ctrl arrastro la linea para enlazar con un View Controller llamado Detalles.
Una vez que pruebo la aplicación la celda me lleva a la vista nueva (Detalles) si selecciono Modal, con Push y Custom no hace nada.
Mi pregunta es la siguiente ¿Cómo puedo detectar desde qué celda estoy llegando a Detalles? Detectar el índice o algo de la celda desde la que llego al View Controller.
Lo que quiero hacer viene a ser como un menú. En detalles detectar la opción desde la que he llegado y mostrar una cosa u otra por ejemplo en un Label. Llevo buscando por ahí y nada.
¿Vendría a ser algo parecido a esto? Label.text = self.navigationItem.title;
Te agradezco si puedes ayudarme.
Le resultó de ayuda?
Me gustaNo me gustaHola Ignacio,
Cuando tu pulsas una celda del Table View se ejecuta un método que se llama didSelectAtIndexPath donde uno de los parámetros que recibe es justamente el indexPath, de donde puedes sacar el número de la fila que ha pulsado.
Usando storyboarding esto es igual.
Mira a ver si con eso te ayuda.
Un abrazo.
Le resultó de ayuda?
Me gustaNo me gustaYa lo tenía solucionado, pero muchas gracias de todas formas Miguel. Un saludo!!
Le resultó de ayuda?
Me gustaNo me gustaBuenas,
Estoy empezando con IOS y me ha servido de mucho este tutorial, pero cuando he llegado a
[self dismissModalViewControllerAnimated:YES];el XCode me dice que:
Receiver type ‘PrimerViewController’ for instance message does not declare a method with selector ‘dimissModalViewControllerAnimated:’
¿Falta algo del código?
Saludos!
Le resultó de ayuda?
Me gustaNo me gustaHola a todos,
Aquellos que tengais problemas con el código, revisarlo bien porque esta verificado que funciona correctamente.
Aitor, el método no se llama “dimissModalViewControllerAnimated”, sino diSmissModalViewCOntroller…”
Un saludo.
Le resultó de ayuda?
Me gustaNo me gustaOstia que fallo más tonto. Mis disculpas y gracias
Le resultó de ayuda?
Me gustaNo me gustaBuenas tardes Miguel, ayer me encontré por casualidad con tu blog y la verdad es que esta noche me ha quitado muchas horas de sueño. Felicidades y sigue así.
Quería hacerte una consulta, ¿exista alguna forma de “abortar” el salto a la nueva vista en tiempo de ejecución? Por ejemplo desde prepareForSegue ó algún otro método delegado.
Muchas gracias.
Le resultó de ayuda?
Me gustaNo me gustaHola Raul,
Muchas gracias a ti por tus comentarios. Animan a seguir con el proyecto, eso desde luego!
No me he topado nunca con ese problema, y a falta de que alguien colabore con algún comentario, te paso un enlace que he visto donde plantean esta misma duda:
http://stackoverflow.com/questions/7819796/how-to-cancel-a-uistoryboardsegue
Si lo resuelves, y sabes!
Muchas gracias de nuevo.
Un saludo.
Le resultó de ayuda?
Me gustaNo me gustaFantástico el artículo, muy bien explicado y muy util.
Me ha ido todo perfecto hasta el metodo prepareForSegue, entonces me he encontrado con el problema de que no se pasan los parametros.
He ido al icono de segue correspondiente y en Identifier he escrito: PantallaDetalle
como tu has dicho he comprobado el controlador con:
if ([segue.identifier isEqualToString:@"PantallaDetalle"]){
}
lo he comprobado con el debug y la condicion se cumple. Dentro del if he escrito:
if ([segue.identifier isEqualToString:@"PantallaDetalle"]){
SegundoViewController * segundoViewController = segue.destinationViewController;
segundoViewController.cajaTexto.text =@”Texto que envio”;
segundoViewController.title =@”Detalle Segundo VC”;
segundoViewController.delegate = self;
}
y no me pone ni el titulo ni nada en la caja de texto, tampoco me devuelve el texto a la etiqueta.
¿Podrias decirme que he hecho mal?
Muchas gracias por adelantado y sigue así!!
Le resultó de ayuda?
Me gustaNo me gustaHola Marc,
¿Puedes enviarme el código a mdiazrub@gmail.com?
Un saludo.
Le resultó de ayuda?
Me gustaNo me gustaClaro, como no!! De momento lo he solucionado creando una propiedad weak y me funciona, de todas formas te lo envio por si hay una forma mejor.
Saludos!
Le resultó de ayuda?
Me gustaNo me gustaHola Marc,
Te he enviado la solución a tu correo. Ya nos contarás si resuelve tu duda.
Un abrazo.
Le resultó de ayuda?
Me gustaNo me gustahola buenas tenia una pregunta
Como puedo crear un controlador del TabBar que sea su delegado y poder hacer que no se pueden pulsar ciertas ventanas hasta que no se cumpla algo me refiero a un controlador que te coja esta evento cuando pulsas en una pestaña
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
Es que en Xcode 4 no se como se hace porque lo consigo hacer todo hasta que llego ha crear los 4 iboutlet de los controladores pero no soy capaz de unirlos con nada en el que es el nuevo interface builder
gracias un saludo
Por cierto tu ejemplo me sirvió perfecto. Una maravilla
Le resultó de ayuda?
Me gustaNo me gusta