¿Cómo usar los Storyboards en UIKit? (Interface Builder)
Los Storyboards nos permiten crear las vistas de nuestra app. Podemos añadir subvistas fácilmente los UIViewControllers y añadir transiciones al usar los Segues. También podemos beneficiarnos del inspector de atributos y otras opciones del Interface Builder
Tabla de contenido
Hoy en SwiftBeta vamos a ver los Storyboards en UIKit. Los Storyboards nos permiten crear de una manera visual, las vistas de nuestra app. Hasta ahora, en el canal habíamos creado todas nuestras vista por código, pero aveces te resultará más fácil crear las subvistas de tu ViewController en el Storyboard, así podrás ver rápidamente qué diseño tiene, cuántas subvistas has añadido, sus contraints, etc.
También te servirá para ver el flujo de tu app, ya que como verás en los próximos minutos, la navegación se ve reflejada en el Storyboard (con unas flechas).
Es importante destacar que puedes tener más de 1 storyboard en tu app (y esto lo veremos también en el video de hoy).
Si quieres apoyar el canal y que siga creando este tipo de contenido, suscríbete. De esta manera seguiré subiendo un video cada semana.
En el ejemplo de hoy vamos a crear dos ViewControllers desde el Storyboard, vamos a crear vistas, navegación etc. Vamos a aprender las características más comunes del Storyboard (y algunos trucos que no son muy conocidos, así que te invito que te quedes hasta el final 😜). Cuando acabes el video podrás decidir si prefieres usar Storyboards o directamente código para crear tus vistas en UIKit.
Creamos Proyecto en Xcode
Lo primero que vamos hacer es crear nuestro proyecto en Xcode. Es muy importante que cuando crees el proyecto, selecciones en Interface Storyboard, ya que hoy vamos a seguir con el curso de UIKit.
Una vez creado el proyecto de Xcode vemos que tenemos varias clases. Una de las más importantes de nuestro proyecto es el fichero ViewController, tal y como vimos en los primeros videos:
- ViewController es la manera que controla UIKit para mostrar distintas vistas dentro de nuestra app. Es una clase que se encarga del ciclo de vida de las pantallas que mostramos (tiene métodos que se llaman automáticamente cuando presentamos una vista, cuando la hemos presentado, cuando va a desaparecer la vista, etc). Normalmente, un ViewController representa una vista entera (pero no siempre es así, ya que podemos tener un ViewController compuesto por diferentes ViewControllers).
Dentro de la vista de un ViewController podemos añadir subvistas como UIButtons, UILabels, UIImageView, etc y de esta manera crear nuestra pantalla.
Si compilamos ahora, el fichero ViewController sería la vista que se mostraría en nuestra app. Pero, ¿cómo podemos modificar la vista de nuestro ViewController y añadir UIButtons, UILabel, etc?
Hasta ahora lo habíamos hecho siempre por código, pero hoy veremos una nueva manera de hacerlo, con los Storyboards. Para ir a nuestro Storyboard (que se crea automáticamente al crear nuestra app con Interface Storyboard), tan solo debemos abrir este fichero, el que pone Main.
Fichero Main (Storyboard)
Una seleccionado nuestro fichero Main, vemos que aparece un único ViewController y la pantalla está en blanco (de momento no hay ninguna subvista en nuestro ViewController). Aquí podemos añadir las subvistas que queramos. Pero ¿cómo podemos saber cuáles están disponibles? Es decir, qué vistas puedo añadir. Para hacerlo, puedes abrir la libería, hay dos maneras:
- Puedes darle a al + que aparece arriba a la derecha
- Pulsar el atajo de teclado COMMAND+SHIFT+L
Vamos a añadir una interface muy sencilla, primero vamos a añadir un UIImageView, y un UIButton. El UIButton nos permitirá navegar a otro ViewController.
La imagen la vamos a sacar de una página de Apple donde obtenemos recursos gratuitos, puedes usar cualquier imagen que tengas en tu ordenador:
y si quieres la dejo aquí por si la quieres descargar directamente:
Vamos a continuar, ahora vamos a arrastrar esta imagen a nuestro proyecto de Xcode. De esta manera la podremos utilizar desde nuestro Storyboard.
Una vez arrastrada, lo que vamos hacer es añadir un UIImageView. Y lo vamos a colocar en la parte superior de nuestro ViewController. Una vez colocado asignamos la imagen que acabamos de añadir. ¿Cómo lo hacemos? clicamos en nuestro UIImageView y en el inspector de atributos añadimos el nombre de la imagen que hemos arrastrado (Si te equivocas de nombre saldrá un placeholder).
Fíjate que en el inspector de atributos aparecen una serie de propiedades, todas estas propiedades puedes modificarlas con un solo click desde el Storyboard. Si hicieras esta misma vista por código, desde dentro del ViewController, podrías acceder a las mismas propiedades que aparecen aquí en el inspector de atributos, por ejemplo: podrías cambiar el Content Mode por código.
Una vez hemos añadido nuestro UIImageView, ahora vamos a añadir nuestro UIButton, para hacerlo, abrímos la librería COMMAND+SHIFT+L y seleccionamos un Filled Button. Una vez seleccionado lo añadimos justo debajo de nuestro UIImageView.
Storyboard es un XML
De momento no hemos añadido ninguna Constraint, es lo que haremos más adelante. Cómo curiosidad quería comentarte que este Storyboard se abre por defecto con el Interface Builder, pero podrías ver el código que hay debajo de esto, un Storyboard en realidad es un XML. Para ver este XML puedes abrir el Storyboard y ver el Source Code de la siguiente manera:
Al hacerlo se puede identificar claramente las dos subvistas que acabamos de añadir a nuestro ViewController (ImageView y Button):
Fíjate que dentro del tag subviews aparece un imageview y un button, cada uno tiene ciertas propiedades. ¿A que es curioso? vamos a volver a mostrarlo con el Interface Builder, de esta manera volveremos a tener la representación visual de este XML y será más fácil trabajar en los próximo minutos 😜
Para abrir el Storyboard con el Interface Builder, seguimos los mismos pasos que antes, pero en lugar de abrir por código, abrimos con Interface Builder.
Una vez explicado esto, vamos a crear otro ViewController, y lo que haremos es que se pueda navegar de un ViewController a otro. Para hacerlo, vamos a crear un nuevo ViewController.
Navegación entre ViewControllers con el Storyboard
Tal y como hemos dicho, vamos a crear un nuevo ViewController, pulsamos COMMAND+N y seleccionamos Cocoa Touch Class, muy importante que la subclase sea de tipo UIViewController y lo vamos a llamar ViewController2:
Una vez creado, debe aparecernos en el listado de ficheros de Xcode. Ahora, nos vamos a nuestro Storyboard y añadimos un ViewController. Y ¿Cómo lo hacemos? Pulsando COMMAND+SHIFT+L. Buscamos ViewController y lo arrastramos al Storyboard.
Ahora tenemos 2 ViewControllers en nuestro Storyboard, pero aún no hemos acabado de configurar el que acabamos de crear:
Es decir, ¿cómo sabe Xcode que la clase que hemos creado hace un momento, la que hemos llamado ViewController2, está relacionado con esta representación visual del Storyboard? La respuesta es que no lo sabe, debemos ayudarle. Y para hacerlo, seleccionamos el ViewController que acabamos de añadir en el Storyboard.
Una vez seleccionado, nos fijamos en el inspector de atributos de Xcode. Aquí, en la sección de Custom Class vamos a Class y añadimos ViewController2 y pulsamos enter.
Perfecto, ya hemos hecho la conexión de nuestro código con la parte visual del Storyboard.
Ahora, para poder navegar entre un ViewController a otro usando Storyboards, vamos a utilizar los Segues, vamos a ver dos maneras, una usando solo Storyboard y la otra usando Storyboard y código.
Navegación usando solo Storyboards
Esta opción es muy simple, tan solo debemos ir a nuestro UIButton de nuestro ViewController y pulsar CONTROL (mientras lo mantenemos pulsado) y arrastrar hasta el ViewController2.
Una vez soltamos, nos aparecen estas opciones. Vamos a escoger Present Modally
Con este paso ya podemos presentar el ViewController2 desde nuestro ViewController. Vamos a probarlo y vamos a compilar. Si pulsas el UIButton, aparece de forma modal nuestro ViewController2. No lo he mencionado, pero esta opción se llama Segue y sirve para crear flujos dentro de tu app. Para navegar de una pantalla a otra.
Un detalle curioso es que podemos cambiar el tipo de transición cuando presentamos ViewController2. Tan solo debemos seleccionar nuestro Segue y ir al inspector de atributos
En la sección Storyboard Segue, verás una opción que es Transition. Aquí puedes cambiarla por Flip Horizontal (o cualquiera de ellas) para ver el cambio en la transición. Si compilar y pulsas el UIButton, verás que aparece una transición diferente al navegar a ViewController2.
Navegación usando Storyboard y código
Para poder tener esta navegación, lo que vamos hacer es borrar la navegación que hemos creado en el paso anterior. Para borrar la navegación tan solo debemos ir al Segue y seleccionarlo, una vez seleccionado le damos a borrar. Muy sencillo.
Ahora, queremos replicar el mismo comportamiento pero por código. Queremos que al pulsar el UIButton se presente de forma modal el ViewController2. Para hacerlo primero debemos crear un IBAction de nuestro UIButton.
En nuestro Storyboard, marcamos el ViewController. Y pulsamos la tecla ALT (OPTION) y clickamos en el ViewController. De esta manera Xcode sale partido por dos, a un lado tenemos el Storyboard y al otro el código del ViewController.
Y ahora lo que vamos hacer es pulsar el UIButton mientras presionamos la tecla CONTROL y arrastramos hacía la la línea 11 de nuestro código.
Una vez soltamos, seleccionamos como tipo de Connection: Action y añadimos de nombre didTapButton, y creamos el IBAction, al hacerlo se nos genera el método que se llamará cuando se pulse nuestro UIButton.
Fíjate que hemos conectado nuestro UIButton con el código de nuestro ViewController. Al hacerlo se ha generado un método y a la izquierda de él aparece un círculo, si está completo es que hay una referencia en el Storyboard.
Al pasar el cursor por encima, podemos saber a qué subvista de nuestro ViewController está conectado.
Un detalle importante es que hemos creado una referencia a la acción que queremos, pero imagina que queremos modificar el UIButton, es decir, cambiar el title, color, etc. Para hacerlo, necesitaríamos crear en lugar de un @IBAction un @IBOutlet, y se crea exactamente igual que el IBAction, solo que en el tipo de Connection seleccionamos Outlet:
Al hacerlo se genera un @IBOutlet como el siguiente:
De momento para resumir, nuestro ViewController tiene el siguiente código:
Ahora, vamos a crear la navegación al ViewController2 cuando el UIButton de ViewController se pulse. Para hacerlo, vamos a escribir el siguiente código, tal y como vimos en anteriores videos:
Vamos a compilar a ver qué ocurre... Si pulsamos el UIButton no se prenseta ningún ViewController, y la razón es que esta vez, al usar Storyboards no podemos hacerlo de la misma manera que lo haríamos por código. Tenemos nuestra representación visual de nuestro ViewController dentro del Storyboard, ahora, tenemos que instancia el Storyboard y cargar el ViewController con un identificador. Vamos paso por paso.
- Vamos a dar un identificador a nuestro ViewController2, este identificador se llama Storyboard ID
En nuestro Storyboard seleccionamos el ViewController2 y nos vamos al inspector de atributos. Allí en la sección Identity añadimo el identificador (puedes añadir el que quieras, pero guardalo ya que lo vamos a utilizar en los siguientes pasos)
- El siguiente paso es irnos al método que hemos creado llamado didTapButton allí vamos a crear una instancia de UIStoryboard y vamos a buscar un ViewController que tenga el identificador que hemos proporcionado hace unos segundos.
@IBAction func didTapButton(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController2 = storyboard.instantiateViewController(withIdentifier: "ViewController2_identifier")
self.present(viewController2, animated: true)
}
Para que quede claro que estamos abriendo el ViewController2, voy a añadir un UILabel rápidamente. Al añadir el UILabel, voy a cambiar el tamaño en el inspector de atributos
Voy a cerrar la pantalla dividida que ha creado Xcode y voy a eliminar el código de navegación ya que más adelante probaremos otra navegación pero con UINavigationController. Cómo podemos eliminar esta conexión del @IBAction entre el Storyboard y el código del ViewController? debemos seleccionar el UIButton e ir al inspector de atributos. Aquí veremos todas las referencias que tiene. En este caso solo queremos borrar la referencia en Sent Events. Una vez eliminada podemos ir al ViewController y vemos que el circulo ya no está rellenado, sale vació eso significa que hemos eliminado correctamente nuestra referencia.
Antes de ver cómo navegar con UINavigationController te voy a mostrar algunas ventajas que también tiene usar los Storyboards.
Accesibilidad, Appearance, Orientation y Devices
Cuando usamos Storyboards podemos ver de un simple vistazo cómo se comportaría nuestra app según estas opciones: Accesibilidad, Appearance, Rotation del device y diferentes devices. Vamos a ver rápidamente cada una de ellas
Accessibility
Aquí podemos ver como se comporta nuestra app si el user tiene habilitado la accedeibilidad, podemos ver rápidamente cómo se comporta nuestra app cuando modificamos el dynamic type, o bold text (vemos en mi caso que tendría que modificar alguna vista para que se adaptara correctamente)
Appearance
Con esta opción podemos ver el Dark mode o el modo normal de nuestra app
Orientation
Podemos rotar las vistas de todos nuestros ViewController a la vez dentro del Storyboard
Devices
Con esta opción podemos cambiar el device que hay en nuestro Storyboard. Por defecto tenemos un iPhone, pero podríamos poner un iPad (Aquí al no tener aún constraints, vemos como la vista se desplaza y no se comporta como queramos)
Una vez explicado esto, imagina que en tu app necesitas añadir un NavigationController, ¿cómo lo haríamos con los Storyboards?
Embed NavigationController usando Stoyboard
Dentro del Storyboard seleccionamos el ViewController que queremos que tenga la NavigationController, en nuestro caso vamos a selecciona el ViewController
Fíjate que ahora aparece un NavigationController y una flecha sale de él hacía el ViewController.
Vamos a añadir un Title en nuestro ViewController. Para hacerlo, seleccionamos nuestro ViewController y vamos al inspector de atributos. Allí, en la sección de NavigationItem, añadimos el título que queramos.
Si ahora compilamos, vemos que aparece el title en nuestro Navigation. Pero si pulsamos nuestro UIButton no hay navegación, no se muestra el ViewController2 (ya que no hemos implementado esta lógica) pues bien, vamos a verlo. Quiero que cuando se pulse este UIButton se navege al ViewController2.
Vamos hacer el mismo proceso que antes, voy a mi UIButton, pulso CONTROL y suelto una vez estoy en ViewController2. Al hacerlo, nos aparece un popup. Tan solo debes selecciona la opción de show y ya estará creada toda la mágia.
Una vez realizado este paso, vamos a compilar y vamos a probar nuestra app. Podemos navegar perfectamente a nuestro ViewController2 y volver al ViewController
Un detalle curioso es la flecha que aparece en nuestro ViewController. Esta flecha representa el punto de entrada de nuestra app. Si tuvieras 10 ViewControllers diferentes, solo 1 de ellos tendria esta flechita, ya que solo puede haber un único punto de entrada. Vamos a probarlo, y vamos a cambiar nuestro Entry Point al ViewController2.
Lo primero de todo que vamos hacer es quitar la selección en el inspector de atributos, en la sección ViewController.
Lo siguiente que vamos hacer es lo mismo, pero esta vez habilitando la opción en nuestro ViewController2. Al hacerlo, la flecha, nuestro Entry Point, se ha modificado automáticamente en nuestro Storyboard.
Si compilamos el proyecto vemos que aparece como ViewController inicial el ViewController2.
Para continuar, vamos a dejarlo como lo teníamos antes, vamos a eliminar el UINavigationController, vamos a volver a presentar modalmente el ViewController2 y vamos a marcar como entry porint el ViewController. De esta manera simplificamos el Storyboard.
User Defined Runtime Attributes
Vamos a ver qué más podemos hacer al usar los Storyboards. Voy a explicar a continuación cambios que podemos hacer en runtime de nuestra app, no soy muy fan de esta práctica pero quiero explicarte en qué consiste.
Dentro del Storyboard, si marcamos el UIButton, y vamos al inspector de atributos. Allí encontramos una sección llamada User Defined Runtime Attributes. En esta sección podemos modificar propiedades cuando lancemos nuestra app, es decir, podemos cambiar propiedades de nuestro UIButton (y de cualquier otra vista). Tan solo debemos especificar la propiedad, el tipo y su valor y cuando lancemos la app, todos los key path añadidos se modificarán. Por ejemplo, vamos a cambiar el color de la fuente de nuestro UIButton y su texto para hacerlo vamos a acceder a:
- titleLabel.textColor
- titleLabel.text
Vemos que nuestro Storyboard no cambia el valor, recuerda que debe ser en runtime, es decir cuando se ejecute la app. Si compilamos, vemos cómo aparecen los cambios que hemos realizado.
Si por equivocación aquí añadieramos una propiedad que no existe en la vista, en este caso en un UIButton, se mostraría un error por consola. La app no crashearía, pero se vería por ejemplo el siguiente error:
2022-08-03 17:31:19.388654+0200 UIKit14_Storyboards[16250:409678] Failed to set (swiftbeta) user defined inspected property on (UIButton): [<UIButton 0x139617760> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key swiftbeta.
Esto es muy potente, pero es un poco delicado. Es muy dificil seguir la trazabilidad de cambios en estas propiedas y que aparte si no estás acostumbrado y tienes un error, puedes estar mucho tiempo buscando el bug en el código y lo tienes métido en esta sección del Storyboard. ¿Se puede mejorar? Sí, y es aquí donde entra en juego @IBDesignable y @IBInspectable. Pero antes, vamos a borrar los cambios de nuestra vista que acabamos de añadir.
@IBDesignable y @IBInspectable
Muchas veces queremos realizar cambios en una vista, y queremos verlos reflejados automáticamente en el Storyboard (muy parecido al canvas que tenemos en SwiftUI, que al hacer un cambio en una propiedad lo vemos reflejado automáticamente en la preview), pero hay aveces que el inspector de atributos no nos da esa flexibilidad ya que hay propiedades que no aparecen, como por ejemplo temas de bordes, shadows, etc.
Lo que vamos hacer es crear una propiedad en una clase que vamos a crear ahora, y que esta propiedad esté visible desde el inspector de atributos. Para hacerlo más simple vamos a crear dos propiedades:
- La primera modificará el backgroundColor,
- La segunda modificará el borde del vista
Para hacerlo, vamos a crear una nueva vista, pulsamos COMMAND+N. Seleccionamos Cocoa Touch Class.
A esta nueva vista la vamos a llamar CustomView, y es muy importante que al crearla, seleccionemos como subclase UIView.
Una vez creada, vamos a picar un poco de código. Vamos a crear estas dos propiedades:
Hasta aquí todo ok! ahora vamos a overridear un método llamado prepareForInterfaceBuilder()
Y vamos a dar el valor por defecto de estas propiedades:
Una vez hecho esto, volvemos al Storyboard y añadimo una UIView. Recuerda que para hacerlo debes abrir la librería COMMAND+SHIFT+L y buscar View. Arrástrala al ViewController y seleccionala. En el inspector de atributos, espeficia que es de tipo CustomView.
Al hacer este post estoy utilizando Xcode 14 beta 4. Si tienes problemas al visualizar la UIView que acabas de arrastrar, cierra Xcode y vuelvelo abrir ya que hay algún bug porque da problemas para visualizar con @IBDesignable.
Al abrir Xcode debería ver lo siguiente en tu Storyboard:
Donde la View con el backgroundColor red es la que acabamos de crear, la CustomView. Si ahora vamos a nuestro código de CustomView y moficiamos el valor por defecto de la propiedad swiftbetaBackgroundColor por .blue, vamos a ver qué ocurre.
Al volver al Storyboard, el backgroundColor de nuestra CustomView se ha actulizado al momento! pero hay mucho más, si queremos poder modificar estos valores desde el inspector de atributos, tan solo debemos usar @IBInspectable en las propiedades que hemos creado de CustomView
Si ahora vamos al Storyboard, seleccionamos nuestra CustomView de ViewController. Dentro del inspector de atributos veremos estas dos propiedades, como si fuera magia!
Si modificamos los valores, los cambios se ven reflejados automáticamente en el Storyboard. A qué está chulo?
Para finalizar, que hemos hablado mucho sobre Storyboards. Vamos a explicar que podemos tener varios Storyboards dentro de una app.
Tener diferentes Storyboards (referencias)
Vamos a crear otro Storyboard que contenga el ViewController2. Los Storyboards están pensandos para contener varios Controllers, en este caso lo voy hacer como ejemplo práctico, pero no tendría mucho sentido crear un Storyboard por ViewController. Tan solo, que si ves que hay flujos de tu app que se pueden añadir a un Storyboard nuevo, que lo tengas en cuenta. Quizás quieres tener por ejemplo el proceso de onboarding, login, registro, recover password, en un Storyboard, y el resto de la app en otro.
Para crear una referencia, abrímos la libería COMMAND+SHIFT+L y buscamos reference.
Pero para poder tener una referencia hacía otro Storyboard, debemos crear uno. Vamos a pulsar COMMAND+N y vamos a crear un Storyboard, lo vamos a llamar Storyboard2
Ahora en nuestro ViewController2, vamos a crear un UIButton (igual que hicimos con ViewController) y este UIButton nos servirá para navegar a un ViewController del Storyboard2
Al crearlo, pulsamos CONTROL y arrastramos hasta Storyboard Reference, allí nos sale un popup. Seleccionamos la opción Present Modally
De esta manera, cuando pulsemo nuestro UIButton en ViewController2, se mostrará el primer ViewController (el que contenga el entry point) del Storyboard2.
Vamos a nuestro Storyboard2 y añadimos el initial view controller.
perfecto, si ahora compilamos nuestra app y navegamos hasta ViewController2. Si pulsamos el UIButton nos muestra el ViewController que aparece en Storyboard2, acabamos de crear una referencia de un Storyboard a otro.
Conclusión
Hoy hemos explorado los Storyboards en Xcode en profundidad, hay muchos más temas de los que podemos hablar, pero como primer video espero que hayas aprendido algo nuevo.
En el próximo video veremos los .xibs, otra manera de crear interfaces de nuestras apps