Ya sabemos que los desarrolladores con pelo en el pecho se pican el código de las vistas a mano y usan algún xib de vez en cuando, cuando no hay más remedio. Que los que usamos los storyboards somos unas nenazas que bebemos Coca-Cola Light y tal, que dan muchos problemas para trabajar en equipo, que hacen demasiada magia, y que, en muchas ocasiones, esa aparente facilidad de uso que aportan dificulta muchísimo la reutilización de las vistas. Los xibs parecen una opción más cuerda a todas luces si no necesitas las tablas estáticas ni preparar el flujo de la aplicación en un plis plas. Sin embargo, ya solo por el mero placer de tener mis vistas gráficamente conectadas y ordenadas, además de por lo fácil de su uso, había decidido no volver jamás los xibs. Como siempre, la solución resultó en tomar lo mejor de los xibs y de los storyboards, y es que empezaron a ser engorrosos de usar, por lo que finalmente me pregunté, “¿no habrá alguna manera más civilizada de utilizarlos?”
Me di cuenta de que la solución al problema es bastante sencilla en realidad, simplemente observando las virtudes de los xibs, un aparente ‘quiero y no puedo’ en los Storyboards.
¿Qué encontrarás en este post?
Toggle¿Por código, con xibs o usando Storyboards?
Ventajas de los xibs:
- Son modulares y reutilizables: Como cada uno está en un archivo es muy fácil trabajar en equipo sin preocuparse por tener conflictos entre versiones.
- Funcionan en ‘manual’. Si se trata de un ViewController y un nib, todo se hace por código y bajo control. Sin magia por debajo.
- Cada pantalla está dentro de un solo archivo y es más fácil localizarlas que dentro de un Storyboard (esta parte ha mejorado bastante con Xcode 6).
Ventajas de los Storyboards frente a los xibs
- Permiten tener tus pantallas una junto a la otra y poder crear flujos entre ellas gracias a las ‘segues’. Esto ofrece mucha más sensación de conjunto cuando trabajas en una sola funcionalidad.
- Permiten crear tablas estáticas. Son ideales para formularios, ya que se crean en tiempo de diseño y manejan muy bien el scroll y los eventos del teclado.
- El tiempo de desarrollo se reduce al tener todas las pantallas a mano y controlar su flujo a golpe de clic. Es algo que después se echa un poco de menos en Android.
- Es la solución (forzada) de Apple. No parece que tengan la intención de que estos dos convivan. Tarde o temprano veremos más herramientas solo disponibles desde storyboards, y muy probablemente los nibs comenzarán a adoptar un sutil filtro de color sepia. Tiempo al tiempo.
Divide y vencerás.
¿Quién ha dicho que no puedas usar los Storyboards como si fueran xibs? Probablemente hayas visto ya archivos de Storyboard únicos con tropecientas escenas no conectadas entre ellas; o, por el contrario, otros literalmente “enredados” en segues.
No hagas eso. Es cierto que las pantallas de tu app no son silos cerrados, que están relacionadas unas con otras. También es cierto que puedes acceder manualmente a ellas mediante su identificador, y por esa razón las guardas en el mismo archivo.
Sin embargo, hay otra vida. Es más sencillo que todo eso. Basta con seguir una serie de pautas:
- Usa tantos como necesites. Muchas veces no tiene sentido que tengas un solo storyboard para todo tu proyecto.
- Separa pantallas reutilizables en un Storyboard diferente. No crees dentro del mismo archivo pantallas genéricas que puedan ser después reutilizadas por diferentes flujos. Al igual que harías con un xib o tus clases, no te repitas (DRY).
- Procura encerrar una funcionalidad concreta en un solo storyboard. Lo ideal que tus flujos hagan una sola cosa, independientemente del controlador del que vengan y a dónde vayan.
No está a menudo justificada la creación de un ‘cajón de sastre’ donde acumular controladores, por muy íntimamente ligados que estén en nuestra app. La mayoría de los flujos incluyen un listado, una vista detalle, y algunas pocas escenas más dependientes de estas dos anteriores (añadir, editar, etc). Por tanto, ¿por qué mantener más de 5 escenas en un mismo archivo si realmente no pertenecen a la misma funcionalidad?
Esta metodología de crear storyboards de no más de 5 escenas tiene varias ventajas:
- Permite que un desarrollador se centre en una determinada funcionalidad del proyecto y no entorpezca el trabajo de otros. Después de todo, una vez que nos hemos puesto a trabajar en una pantalla en concreto, lo habitual es alterar solo las que están inmediatamente conectadas a ella. Esto se resume en muchos menos problemas con el control de versiones.
- Cuando el proyecto es muy grande, facilita el separar las diferentes secciones de la aplicación en módulos más esenciales y así reaprovechar el trabajo sin duplicar elementos. Es más, no hay ningún inconveniente en tener una sola escena dentro de un storyboard. Si es funcionalmente independiente no debería ‘atarse’ a otro(s) solo por ahorrarnos el tener un archivo más. Y al igual que ocurre con nuestras clases, no sabemos cuándo ‘crecerán’.
- Los archivos .storyboard se vuelven mucho más ligeros, y por ende, es más ágil trabajar con ellos (especialmente en macs con unos cuantos añitos).
- Siempre podemos seguir saltando entre storyboards por código y olvidarnos de los ‘prepareForSegue’. De hecho, lo ideal sería utilizar este método solamente cuando nos encontramos dentro de la misma funcionalidad, como he mencionado más arriba.
Un ejemplo práctico
Supongamos que estamos desarrollando una app para que los usuarios puedan bajarse canciones. Tendríamos lo siguiente:
- Cuatro flujos diferentes: Artistas, Álbumes, Canciones y el Proceso de compra. Cada uno en un archivo Storyboard distinto.
- Una pantalla para pedir el PIN o contraseña (si hiciera falta).
- Un asistente de bienvenida para iniciar sesión y conocer las funcionalidades de la app.
Esto hace un total de 6 archivos. ¿Qué ventajas puede tener esto? De entrada, la pantalla de bienvenida podría aparecer solo una vez si el usuario nunca borra la app. Evidentemente, no tendría sentido tenerla junto a las otras. Por el contrario, la pantalla con el campo para introducir contraseña sería muy recurrente, ya que la usaríamos para realizar cualquier transacción. De no estar separada en otro archivo tendríamos que replicarla en cada uno de los siguientes flujos: Artistas, Álbumes, Canciones y Proceso de Compra. Ni de coña. Las tres primeras podrían estar dentro de un TabBar (crear uno en el Storyboard es una pérdida de espacio asombrosa y no aporta demasiado), y la última, el Proceso de compra, ser llamada cuando corresponda, en cualquier momento.
¿Cómo? Simplemente creando una instancia del Storyboard en el que está, y a continuación otra del controlador; es decir, en este caso la pantalla que inicia el flujo de compra. Después le inyectamos los datos del modelo que necesita, hacemos un ‘push’ o mostramos una vista modal de toda la vida (sin segue), y listo.
Esto es posible gracias a que, en el fondo, un Storyboard se comporta como un paquete de xibs. Debería corresponderse más a las carpetas que creábamos antes para agruparlos que a un guión de George Lucas. No importa qué escena cargues ni de dónde. Simplemente aprovecha el poder de las ‘segues’ para mover una entidad de tu modelo por una funcionalidad concreta. Retoma la navegación ‘manual’ cuando necesites cargar flujos independientes, y aprovecha que puedes elegir cómo quieres mostrar la información al usuario (avanzas jerárquicamente o le interrumpes) por código.
Implementación de este sistema
Decidí hace un tiempo crear una serie de métodos que me permitiesen utilizar los Storyboards como paquetes, siguiendo un poco esta filosofía de usar muchos pero más pequeños. La idea era poder manejarse entre ellos como se hacía hasta hace poco con los xibs, utilizando el ‘push’ o ‘present’, sin perder la posibilidad de utilizar el patrón de Inyección de Dependencia de manera sencilla. Bueno, eso y trabajar más rápido, que en el fondo es la esencia de todo esto.
Sauron, una clase para dominarlos a todos y atarlos a… eso
Sauron es un pequeño conjunto de métodos que facilita mucho la navegación entre diferentes storyboards y permite utilizar inyección de dependencia mediante bloques, devolviendo el control al programador y desacoplándolo del storyboard.
¿Cómo funciona?
Con los xib es necesario instanciar el controlador apropiado y enviar el mensaje a nuestro navigationController. Tal que así:
MyNextVC *nextVC = [MHJCharacterVC alloc] initWithNibName: @“myXibName" bundle:nil]; [self.navigationController pushViewController:nextVC animated:YES];
Con Sauron hacer un ‘push’ a un controlador de un Storyboard cualquiera es igual de sencillo.
[self pushToStoryboardNamed:storyboardName withViewIdentifier:nil returningViewController:^(id nextVC) { [nextVC setTitle:@"New Title"]; NSLog(@"[%@] Pushing to %@!", [self class], nextVC); }];
o, utilizando sus métodos de clase,
[MHJSauron pushToStoryboardNamed:storyboardName withViewIdentifier:nil fromViewController:self returningViewController:^(id nextVC) { [nextVC setTitle:@"New Title"]; NSLog(@"[%@] Pushing to %@!", [self class], nextVC); }];
Para hacer más agradable el manejo de la librería, yo recomiendo declarar los nombres de los storyboards como constantes en un archivo de cabecera, e incluirlos en el precompilado de la app. No es una bala de plata, pero ayuda más de lo que estorba.
Para no explayarme más he colocado la descripción de estas clases en GitHub, por si alguien las encuentra de utilidad.
En definitiva
Al igual que separamos nuestras clases por responsabilidades, la idea consiste en hacer lo propio con las vistas. Esto mejorará el trabajo en equipo, la reutilización, nos ayudará a organizar mejor nuestra aplicación, ahorraremos tiempo, y reduciremos la posibilidad de tener storyboards monolíticos.
¿Quieres saber más de storyboards? ¡Mira nuestro bootcamp de desarrollo mobile!