# sesion-1-guias-de-estilo-y-personalizaciones-avanzadas

\# 1- Guías de estilo y personalizaciones avanzadas

En esta primera sesión veremos los patrones de diseño que Apple nos recomienda seguir para nuestras aplicaciones. En algunos casos es obligatorio cumplir estos patrones y el hecho de no hacerlo puede ser motivo de rechazo para publicar en el *App Store*. Por último implementaremos dos ejercicios de personalización de controladores.

### Guías de estilo en iOS

Tanto el iPhone como el iPad supusieron un revolucionario giro en cuanto a diseño de interfaz. Para evitar el caos a la hora de diseñar cualquier tipo de aplicación, tras cada actualización de iOS *Apple* plantea una serie de guías de estilo que todo desarrollador debe seguir en la medida de lo posible para diseñar una app. Estas guias de estilo no se aplican, como es lógico, a la hora de diseñar la interfaz de un juego, pero son muy útiles para crear aplicaciones.

En esta sesión vamos a aprender a diseñar apps para iPhone / iPad que sean usables, accesibles y en las que el usuario perciba una buena experiencia de uso. Hay que tener en cuenta que un usuario de iOS está habituado a una serie de elementos y situaciones básicas, como la navegación dentro de vistas mediante *Navigation Controllers*, navegación por las opciones mediante los *Tab Bar Controllers*, uso de botones, etc. Aquí haremos un resumen de todo lo que Apple propone en sus [guias de estilo](https://developer.apple.com/ios/human-interface-guidelines/). En esta asignatura nos centraremos en los aspectos principales de la guía, aunque es recomendable leer por completo las guías de estilo tras cada actualización de iOS, ya que las guías cambian de un año para otro.

Además de la guía de estilo oficial, conviene consultar detalladamente este enlace: [iOS Design do's and don'ts](https://developer.apple.com/design/tips/).

En esta sección veremos 3 puntos principales:

* Principios de diseño
* Características de la plataforma iOS
* Guías de uso de las principales tecnologías disponibles en iOS

#### Principios de diseño

*Apple* recomienda diseñar apps siguiendo estos principios:

* **Claridad**. En nuestras apps el texto debe poder ser legible a cualquier tamaño, los iconos deben ser precisos y claros, los adornos poco recargados y apropiados, y debemos centrarnos principalmente en la funcionalidad. Los espacios negativos, colores, fuentes, gráficos y elementos del interfaz deben resaltar el contenido que es importante y facilitar la interacción.
* **Amigabilidad**. Un interfaz fluido, claro y visualmente atractivo ayuda a los usuarios a entender e interactuar con el contenido, que normalmente ocupa la pantalla completa. Es importante mantener el interfaz ligero y claro para que el contenido destaque mejor.
* **Profundidad**. Las distintas capas visuales y el movimiento realista deben facilitar la comprensión de la jerarquía del interfaz e imprimir dinamismo. Tocar y descubrir mejora la sensación de interacción y permite el acceso a funcionalidades y contenidos adicionales sin perder el contexto. Las transiciones proporcionan una sensación de profundidad mientras se navega por el contenido.

En general, lo principal que deben tener las aplicaciones es:

* Integridad estética: debemos integrar adecuadamente el aspecto visual con su funcionalidad.
* Consistencia: deben usarse elementos estándar a los que ya están habituados los usuarios de iOS y un diseño uniforme durante toda la app.
* Adaptabilidad: es muy recomendable que el contenido de la pantalla cambie cuando se rota el dispositivo o se usan gestos. Las vistas deben adaptarse a cambios de orientación de dispositivo, modo oscuro (*dark mode*) y tipos dinámicos, dejando que los usuarios elijan las configuraciones que prefieran desde la configuración general del dispositivo.
* *Feedback*: se deben confirmar las acciones y mostrar resultados para informar al usuario de que ha interactuado con nuestra app.

Antes de comenzar a diseñar una app también es muy importante conocer quiénes son los usuarios a los que nos dirigimos, es decir, el público objetivo, y en función de esto definir la interfaz gráfica, los controles, terminología, etc. No es lo mismo diseñar una app para niños que una de contabilidad, por ejemplo.

Siempre es recomendable hacer un diseño previo usando Mockups, *storyboards* u otras herramientas antes de que la app sea funcional. Existen varios programas de Mockups, como [Play](https://createwithplay.com) o [Balsamiq](https://balsamiq.com).

La aplicación que diseñes debe estar totalmente adaptada a un dispositivo iOS, no a web u otro tipo. El usuario lo agradecerá (y Apple, cuando lo revise, también). La inmensa mayoria de usuarios de iOS están acostumbrados al uso de botones, barras de navegación, *Tab Bars*, etc. Por tanto, en la medida de lo posible debemos hacer uso de toda esta serie de componentes que han sido diseñados de forma específica para dispositivos iOS y con los que los usuarios están muy familiarizados.

En [este enlace](https://developer.apple.com/design/human-interface-guidelines/components/all-components) se pueden ver todos los componentes que podemos usar en nuestras apps. Se dividen en:

* Contenido. Estos componentes sirven para mostrar gráficos, texto, imágenes, o contenidos web.
* Distribución y organización. Elementos como las colecciones, tablas o etiquetas pueden usarse para organizar y mostrar los datos.
* Menús y acciones. Para interaccionar con el usuario disponemos de botones, menús o pop-ups, entre otros.
* Navegación y búsqueda. El usuario puede moverse por nuestra app usando barras de navegación, *tab bars* o usando campos de búsqueda, entre otros.
* Presentación. Para presentar los datos podemos usar alertas, controles de página, popovers, vistas de scroll, etc.
* Selección y entradas. Entre otros, se pueden elegir valores de una lista con *pickers*, usando *segmented controls*, o emplear *sliders* para valores numéricos.
* Estado. El estado de una tarea se puede medir con barras de progreso, anillos de actividad o medidores (*gauges*).
* Experiencias del sistema. El usuario puede recibir notificaciones, puede ver la barra de estado de la app, usar *widgets*, etc.

Todos los componentes pueden consultarse con más detalle en la referencia del [catálogo de UIKit](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/UIKitUICatalog/index.html).

Es importante que las aplicaciones funcionen correctamente en todos los dispositivos donde puedan instalarse. También hay que asegurarse antes de publicar en *App Store* de que no hay *bugs*, ya que si estos existen Apple puede rechazar la aplicación, o si aparecen después los usuarios podrían puntuarla mal o dejar de usarla. Existen una serie de puntos a tener en cuenta para esto:

* Hay que diseñar cada aplicación según el dispositivo en el que se use teniendo en cuenta especialmente las capas de vistas, ya que su aspecto puede cambiar bastante en función del tamaño de pantalla disponible.
* Se debe de adaptar todo el arte (imágenes, videos, etc.) al dispositivo en cuestión. Para esto se usan los *Assets*. En las últimas versiones de iOS, se recomienda usar ficheros PDF en los *Assets*, ya que se adaptarán a cualquier tamaño.
* Las características principales de la aplicación deben conservarse independientemente del tipo de dispositivo que se use.
* Cuando sea posible, es conveniente diseñar las apps para que funcionen en todos los dispositivos Apple (iOS, iPadOS, watchOS, tvOS y macOS).

#### Características principales de la plataforma iOS

**La pantalla**

Todo usuario que usa una aplicación interactúa mediante la pantalla. Existen distintas resoluciones de pantalla (a nivel de desarrollo de código y a nivel físico) según cada dispositivo. Se pueden consultar consejos de diseño y la resolución de todos los modelos de Apple en [este enlace](https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/).

Es importante resaltar que el tamaño en puntos no es el mismo que el tamaño en píxeles, que normalmente es un múltiplo (2x, 3x) de los puntos, como puede verse en el siguiente ejemplo:

![Resolución iPhone](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LDGNOA4SEPtKxESUKUy%2F-LDGNOtSnuTwY6m892S2%2F-LDGNRh3XAFpYiucTvtB%2Fdesign-Resolutions.jpg?generation=1527152545589258\&alt=media)

> Nota sobre la detección de contacto (touch events): El tamaño de la zona mínima para que el evento de contacto funcione correctamente debe de ser de al menos 44 x 44 puntos.

Dada la diferencia de resolución entre los distintos dispositivos de Apple, se recomienda usar siempre *autolayout* o *SwiftUI* en lugar de indicar explícitamente coordenadas de la pantalla donde colocar los componentes.

> **SwiftUI** es el nuevo sistema de Apple para el desarrollo de interfaces. Tiene como principal ventaja sobre el sistema tradicional (*UIKit*) que se ahorra código al usar sintaxis declarativa. También facilita que la app funcione correctamente en todos los dispositivos debido a las mejores sobre el *autolayout* de *UIKit*. Sin embargo, en esta asignatura todavía no vamos a introducir este entorno porque es muy reciente, todavía tiene bugs y está sujeto a cambios frecuentes que pueden hacer que las apps no compilen correctamente en pocos meses. De todos modos, si quieres más información sobre *SwiftUI* puedes consultar [este enlace](https://developer.apple.com/xcode/swiftui/).

**Orientación del dispositivo**

Uno de los requisitos a la hora de publicar una aplicación de iPhone/iPad en el *App Store* es la compatibilidad con las distintas orientaciones que puede adoptar el dispositivo iOS, tanto vertical (*portrait*) como horizontal (*landscape*). Cualquier aplicación debe estar adaptada a ambas posiciones para mejorar la usabilidad. La programación de la orientación en una aplicación iOS es relativamente sencilla (excepto en casos puntuales) y no implementarla puede suponer, como hemos comentado, el rechazo de su publicación en el *App Store*.

A continuación puedes ver orientaciones de ejemplo para un iPhone y su layout:

![Resolución iPhone](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LDGNOA4SEPtKxESUKUy%2F-LDGNOtSnuTwY6m892S2%2F-LDGNRhDdJcdgagA4TCm%2Fdesign_iphone.jpg?generation=1527152510523589\&alt=media)

Y para el iPad:

![Resolución iPhone](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LDGNOA4SEPtKxESUKUy%2F-LDGNOtSnuTwY6m892S2%2F-LDGNRhF6shUKbAkDBlL%2Fdesign_ipad.jpg?generation=1527152546125831\&alt=media)

***Storyboards***

Con `UIKit` se suelen diseñar las apps usando un `Main.storyboard` (aunque también puede hacerse desde código). Para la pantalla de inicio que se muestra brevemente al arrancar la aplicación, a veces interesa implementar otro *storyboard* adicional (`LaunchScreen.storyboard`) si el proceso de carga de la app es lento. Las nuevas apps basadas en `SwiftUI` no usan *storyboards*.

**Gestos**

Llamamos gestos a los distintos movimientos que hace el usuario sobre la pantalla para realizar acciones. En la última sesión veremos en detalle cómo gestionar los gestos sobre la pantalla.

**Ayuda**

Una aplicación debe ser lo suficientemente simple e intuitiva para que el usuario sepa usarla. Hay que tener en cuenta que el usuario no tendrá tiempo ni ganas de estar leyendo un texto de ayuda la primera vez que usa la aplicación. Lo ideal es que no sea necesario ninguna ayuda, pero si existe debe ser lo más clara y compacta posible, y si se pueden utilizar imágenes o videos, mejor.

**Sonidos**

A veces nos interesa reproducir sonidos en nuestra app, bien porque es necesario o para llamar la atención del usuario. Sin embargho hay que tener en cuenta lo siguiente:

* Si el usuario activa el modo *silencio* en el dispositivo, este no debe de emitir ningún tipo de sonido. Hay apps que se saltan esto, y suele ser algo bastante molesto. Si nuestra app se basa completamente en el sonido (por ejemplo, un reproductor musical) puede que tengamos que hacer alguna acción (por ejemplo pausar la música) cuando se silencie el móvil.
* El tipo de sonido escogido para las distintas características de la aplicación es muy importante. Estos sonidos deben ser adecuados y deben ajustarse al tipo de acción que se esté ejecutando.
* Hay que tener también en cuenta la gestión de las interrupciones de audio. Por ejemplo, puede producirse una llamada entrante, la aplicación pasar a estado inactivo (*background*), etc.
* Si estamos desarrollando una aplicación musical, para mejorar la experiencia de usuario también podemos usar el control remoto de Apple o el sistema *AirPlay*.

**Accesibilidad**

iOS ofrece funcionalidades de accesibilidad para usuarios con discapacidades visuales o auditivas. Con poco esfuerzo y usando los componentes de *UIKit* podemos adaptar nuestra app para que todos los usuarios puedan usarla. Para esto se recomienda:

* Proporcionar etiquetas de texto alternativas para imágenes, iconos y elementos del interfaz. Estas etiquetas alternativas no serán visibles en la pantalla, pero permitirán al sistema de voz (*VoiceOver*) describir qué hay en la pantalla, ayudando a las personas con problemas visuales.
* Responder a las opciones de accesibilidad. Debemos permitir a *UIKit* implementar su interfaz de usuario, para que los elementos se adapten a las preferencias de accesibilidad como texto en negrita o más grande, o eliminar animaciones. Debemos usar la fuente de texto del sistema cuando sea posible.
* Probar la app en modo accesible, para ver cómo queda.
* Usar suficiente contraste para que los iconos y el texto sean fácilmente distinguibles.

**Carga**

Si usamos una pantalla estática mientras se está cargando contenido, puede parecer que la app se haya bloqueado. En lugar de esto, hay que dejar claro que la carga está en marcha, por ejemplo usando barras de progreso o animaciones.

**Preferencias**

Si la aplicación utiliza preferencias propias, podemos hacer una vista para ellas dentro de nuestra aplicación en el caso de que el usuario necesite cambiarlas con frecuencia, o bien añadir un *settings bundle* si el cambio no es tan frecuente. Un *settings bundle* contiene ficheros (principalmente *.plist*) que describen la estructura y estilo de presentación de las preferencias, y es muy sencillo de implementar. La app *Settings* usa esta información para crear una entrada para nuestra aplicación y mostrar las preferencias.

![Preferencias](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LDGNOA4SEPtKxESUKUy%2F-LDGNOtSnuTwY6m892S2%2F-LDGNRhXipDrdvKicGrH%2Fpreferencias.png?generation=1527152548017800\&alt=media)

#### Guías de uso de las principales tecnologías disponibles en iOS

La API de iOS nos da acceso a múltiples tecnologías nativas que se describen a continuación:

**Multitarea**

Mediante la multitarea nuestro dispositivo puede ejecutar más de dos aplicaciones al mismo tiempo. Otras tareas que no estén mostrándose en pantalla pueden estar guardadas en memoria (en *background*) o también mostrándose simultáneamente en la pantalla. Apple recomienda que nuestra aplicación tenga en cuenta dicha característica ya que de esta forma el usuario puede estar realizando otras tareas al mismo tiempo sin necesidad de cerrarla.

![Captura multitarea iOS](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-a46967596a82e13e6062fdc19d8733003dbc86ea%2Fmultitasking3.png?alt=media)

La aplicación debe estar preparada para gestionar interrupciones de audio en cualquier momento, pararse y reiniciarse sin ninguna complicación ni lag y de forma "suave", y debe comportarse de forma adecuada cuando se encuentra en *background*. La multitarea es una tecnología que se utiliza muy a menudo en iOS y es por ello que debemos tenerla presente cuando diseñamos nuestras aplicaciones. La gestión del paso de un estado *activo* a *inactivo* debe programarse cuando sea necesario. Por ejemplo, hay que tener en cuenta que nuestro juego Angry Birds puede interrumpirse por una llamada a mitad del lanzamiento de un pájaro.

Para determinar el comportamiento de nuestra aplicación cuando entra en modo *background* podemos implementar métodos que se verán en otras asignaturas del máster.

Además de tener que estar preparados para interrupciones, la multitarea implica que nuestra app puede ejecutarse sólo en una zona de la pantalla, por lo que hay que tener en cuenta esto en la fase de diseño.

**Notificaciones**

Las [notificaciones *push*](https://developer.apple.com/documentation/usernotifications) permiten avisar a los usuarios independientemente de si la aplicación está funcionando en ese momento. Esto es muy útil en apps que usen un calendario, para avisar de eventos futuros, o para algún tipo de recordatorio. También se utiliza muy a menudo en juegos.

![Notificación push](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-6b89e072a11e72fd3d29ca2a7c297177a4cccbd2%2Fnotificaciones_push.jpg?alt=media)

Se pueden enviar notificaciones usando un servidor propio, o bien generarlas localmente desde nuestra app. En el caso de requerir un servidor también se pueden contratar servicios externos que realicen esa tarea de forma bastante económica.

**Extensiones**

Con las extensiones los usuarios pueden usar un componente desarrollado por nosotros desde una app externa. Por ejemplo, mientras un usuario ve una página web con Safari, podría usar nuestras extensiones para enviar una imagen o un artículo a nuestra red social. O podría también usar desde la aplicación de fotos de Apple una extensión nuestra para editar la imagen.

Las extensiones pueden ser de tipo *widget*, para realizar acciones desde el notification center, *share* para compartir contenido con terceros, *action* para ver contenido desde otra aplicación, *photo editing* para editar imágenes, o *custom keyboard* para reemplazar el teclado de iOS por uno personalizado, entre otras posibilidades.

![Widget](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LDGNOA4SEPtKxESUKUy%2F-LDGNOtSnuTwY6m892S2%2F-LDGNRhqZrq6ZMaz5zXl%2Fwidget_experience_2x.png?generation=1527152544919730\&alt=media) ![Action](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LDGNOA4SEPtKxESUKUy%2F-LDGNOtSnuTwY6m892S2%2F-LDGNRhsnhwhfnP8CDWq%2Fshare_action_appex_2x.png?generation=1527152516480671\&alt=media)

Básicamente estos son los conceptos principales de la guía de estilo de Apple, aunque hay más tecnologías que pueden verse en su [guía de estilo](https://developer.apple.com/ios/human-interface-guidelines/).

***

### Personalizaciones avanzadas

Las personalizaciones son las modificaciones sobre el aspecto visual de los componentes estándar de Apple. Por lo general las personalizaciones no se recomiendan para evitar confundir al usuario, pero a veces son necesarias para juegos o aplicaciones que requieran un aspecto diferente, como puede verse en el siguiente ejemplo:

![Personalización](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LDGNOA4SEPtKxESUKUy%2F-LDGNOtSnuTwY6m892S2%2F-LDGNRimnOu3RVPPazNa%2Fcustomization.png?generation=1527152547981596\&alt=media)

Vamos a empezar programando ejemplos de personalización de controladores.

### Ejercicio 1- Personalización de celdas

La siguiente imagen muestra un ejemplo de una tabla con las celdas totalmente personalizadas.

![Ejemplo personalización celdas](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-5dc073780035626750deefe69f6aa3cef9a9aac8%2Fcustom_cells.png?alt=media)

A diferencia de la personalización de otros componentes, la personalización de celdas en las tablas es algo muy habitual en iOS. De esta forma podemos hacer que nuestras tablas se distingan del resto, mostrar datos de forma más adecuada, y ofrecer un aspecto característico para nuestra app.

¿Cómo podemos hacer este tipo de celdas? Es muy sencillo, a continuación veremos mediante un ejemplo paso a paso el diseño y programación de celdas personalizadas mediante XCode.

#### Creando el proyecto y las clases básicas

Al terminar este ejemplo tendremos una aplicación que solo contiene una vista de tabla `UITableView` con celdas personalizadas. Estas celdas tendrán una imagen en el lado izquierdo, un texto en negrita en la parte superior y otro texto pequeño en la parte inferior. Las celdas tendrán un tamaño algo mayor al que viene por defecto y, para finalizar, la tabla tendrá estilo *zebra*, es decir, el fondo de las celdas tendrá colores intercalados para cada fila.

Comenzaremos creando el proyecto. Para ello, abrimos XCode y creamos un nuevo proyecto para *iOS* de tipo *App*. Lo guardaremos con el nombre `ejercicio_celdas` seleccionando el interfaz *Storyboard*:

![Inicialización ejercicio celdas](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-42e927ce4125942fd4278190f8b3467ddbc8c730%2Fcell_init.png?alt=media)

Para empezar vamos a añadir al *storyboard* principal (*Main*) un nuevo controlador (usando el botón **+** en la parte superior del interfaz de XCode) de tipo `Table View Controller`.

Ahora haremos que nuestra tabla sea lo primero que aparezca cuando se inicie la app. Esto se puede hacer, por ejemplo, arrastrando la flecha horizontal que apunta al primer controlador hacia nuestro nuevo *Table View Controller*. Ya podemos borrar la vista vacía del *storyboard*, que ahora comenzará con nuestra tabla. Podemos también borrar el fichero `ViewController.swift`, ya que estaba asociado a la vista que hemos eliminado.

Ya tenemos la vista de la tabla. Ahora vamos a crear también un fichero de código como controlador para poder programar los elementos de la celda. Seleccionamos *File > New > File > Cocoa Touch Class*, y le damos el nombre `TableViewController`, subclase de `UITableViewController`, dejando sin marcar "*Also create XIB file*".

Ahora tenemos que asignar la vista al controlador. Para ello, seleccionamos el controlador en el storyboard, y desde el *Identity Inspector* le asignamos la clase que hemos creado `TableViewController`, como se muestra en la imagen:

![Asignación del controlador TableViewController](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-63d5ebd3490f23e88c08d8dd5c75a17484197be1%2Fcell_tableviewcontroller.png?alt=media)

Por último, para nuestra vista personalizada vamos a crear un nuevo fichero subclase de `UITableViewCell`. Hacemos click en *File > New > File > Cocoa Touch Class*, y seleccionamos Subclass of `UITableViewCell`, dejando sin marcar "*Also create XIB file*". Lo guardamos con el nombre `TableViewCell`.

#### Diseñando la celda desde Interface Builder

Una vez hemos creado el proyecto y la clase controladora de la tabla y la celda vamos a diseñar su vista. Para ello abrimos la tabla en el *storyboard*. Dentro de *Prototype cells* podemos diseñar la vista de la celda, pero antes tenemos que asignar a las celdas de nuestra tabla la clase `TableViewCell` que hemos creado.

![Asignación de la celda personalizada](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-b9f466a6baa71cbda7be7d6dac25413d6a7c4f2d%2Fcell_custom_class.png?alt=media)

Una vez hemos vinculado la vista de la celda a nuestro controlador vamos a modificar la altura de la celda desde el *size inspector*, usando una altura de 70 puntos:

![Ajustes de tamaño vista celda](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-971aa3b9b8be6bcaab0211b665e9157a6a640940%2Fcell_table_view_row1.png?alt=media)

Además de indicar la altura de la celda para el `Table View Cell` (lo cual nos sirve para saber cómo quedan los elementos en la vista de diseño), también debemos hacerlo para las celdas del `Table View`:

![Ajustes de tamaño vista tabla](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-c5579a40fcd1a15ac99da58c9ac600639a015637%2Fcell_table_view_row2.png?alt=media)

Si quisieramos hacer lo mismo pero desde código, podríamos indicarlo así en el método `viewDidLoad`:

```swift
 self.tableView.rowHeight=70
```

Ahora solo tenemos que arrastrar los objetos que queremos que aparezcan en la celda. En este ejemplo vamos a añadir dos etiquetas (`UILabel`) y una imagen (`UIImageView`). La celda quedará así en el *Interface builder*:

![Vista celda](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-0c4e6cbc443f5289ebb667214f97512c3ddde414%2Fcell_myTableViewCell.png?alt=media)

En la pestaña de *Attributes Inspector* de la celda escribimos un identificador para ella: `TableViewCell`. Esto servirá más adelante para referenciarla desde el controlador de la tabla y así poder utilizar las celdas desde este controlador:

![Identificador en la celda](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-8ac555f8f68107e25e4492a25f58f2689572eeca%2Fcell_attributes.png?alt=media)

Ya hemos diseñado la vista de la celda. Ahora vamos a programar el código de su controlador.

#### Programando la celda

Tendremos que vincular a nuestra clase celda los *Outlets* que hemos creado antes en la vista. Para ello abrimos el fichero *TableViewCell.swift* y añadimos las siguientes propiedades a la clase:

```swift
@IBOutlet weak var imagen: UIImageView!
@IBOutlet weak var labelTitle: UILabel!
@IBOutlet weak var labelAuthor: UILabel!
```

> Alternativamente, se pueden crear los IBOutlets con el asistente (a veces es más cómodo). Pinchando sobre el símbolo que representa una tabla con un *+* dentro de ella (arriba a la derecha), se abre una segunda ventana de edición. Así se puede seleccionar la vista en una de las ventanas, y el código en otra. Después, puedes pinchar (botón derecho) sobre el componente de la vista y arrastrarlo al código de la declaración de la clase. Esto crea automáticamente las propiedades, y genera los enlaces. El mismo procedimiento es válido también para crear un `IBAction`.

Ahora volvemos a la vista de la celda en el *storyboard* y enlazamos los *Outlets* creados en la clase con los objetos de la vista.

Una vez hecho esto abrimos de nuevo la clase controladora de la tabla `TableViewController.swift` y modificamos los siguientes métodos:

```swift
 override func numberOfSections(in tableView: UITableView) -> Int {
     return 1
 }

 override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
     return 3
 }

 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
     let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as! TableViewCell

     cell.labelTitle?.text = "Título \(indexPath.row+1)"
     cell.labelAuthor?.text = "Autor"
     cell.imagen?.image = UIImage(named : "logo-master.png")

     return cell
 }
```

Descarga la imagen [logo\_master.png](https://github.com/mastermoviles/interfaz-usuario-ios/blob/master/docs/gitbook/assets/logo-master.png) y arrástrala dentro de XCode a los *Assets* del proyecto.

En este momento ya podemos ejecutar la aplicación y nos debe aparecer la tabla con las celdas que acabamos de programar. Si la ejecutamos en el mismo dispositivo que teníamos seleccionado en la barra inferior (sobre el cuál hemos hecho el diseño), nos saldrán los elementos relativamente bien colocados. En cambio, si la ejecutamos con el simulador para otro dispositivo distinto, probablemente se nos descoloquen los elementos de la celda porque falta ajustar los *constraints* (de hecho se mostrará algún *warning* por este motivo). De momento no te preocupes por esto, lo arreglaremos más adelante.

#### Añadiendo la barra de navegación

La aplicación funciona se muestra una tabla, pero no tiene barra de navegación. Normalmente todos los controladores en iOS suelen ir incrustados en controladores contenedores, que son los que gestionan la navegación, como `UINavigationController`, `UITabBarController` o `UISplitViewController`.

Para nuestro ejercicio, lo más adecuado incrustrar la tabla en un `UINavigationController`, seleccionando el controlador de la tabla en el storyboard y eligiendo *Editor > Embed in > Navigation Controller*. Añade un título para la tabla mediante la siguiente línea de código en `viewDidLoad` de `TableViewController` (o si lo prefieres, desde el *Interface Builder*):

```swift
    self.title = "Mi tabla personalizada"
```

#### Personalización con efecto zebra

Una vez tenemos las celdas vamos a personalizar la tabla un poco más, añadiendo un fondo a las celdas pares y otro a las impares, lo que se conoce como efecto *zebra*. Para esto necesitaremos dos imágenes más que se pueden descargar [aquí](https://github.com/mastermoviles/interfaz-usuario-ios/blob/master/docs/gitbook/assets/img_fondo_celdas.zip) y añadirlas a los *Assets*.

Abrimos la vista de la celda en el *storyboard* y arrastramos un `UIImageView` ocupando todo el espacio de la celda. Es necesario enviar esta vista al fondo, con el menú *Editor > Arrange > Send to back*. Si en el modo visual se van descolocando las vistas de la celda cuando las mueves (aunque esto no debería ocurrir con las últimas versiones de XCode), ajusta los tamaños desde el *Size Inspector*.

En el fichero `TableViewCell.swift` enlazamos el nuevo `UIImageView`, como hemos hecho con el resto de elementos, y lo llamamos `fondo`.

Ahora, en la clase `TableViewController`, dentro del método `cellForRowAt` añadimos lo siguiente justo antes de `return cell`

```swift
if indexPath.row % 2 != 0 {
    cell.fondo.image = UIImage(named:"fondo_celda1.png")
}
else {
    cell.fondo.image = UIImage(named:"fondo_celda2.png")
}
```

Si ejecutamos ahora el proyecto tendremos nuestra tabla con las celdas personalizadas:

![Celdas final](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-c4ef90a2d04e6679b03a11fe9312cd53a03e725d%2Fcell_final.png?alt=media)

#### Ajuste de las constraints

Sólo hay un problema: como hemos comentado anteriormente, se ha diseñado la vista para un iPhone en concreto (iPhone 14 Pro por defecto), y si ejecutamos el simulador con otro iPhone (por ejemplo el SE) los elementos de la celda nos saldrán descolocados. Para que salgan bien nos falta ajustar las *constraints* de la vista.

Para esto, seleccionamos el `TableViewCell` desde el *storyboard*, y en la parte inferior ("All Views in Table View Cell") marcamos "All Views in TableViewController" -> "Reset to Suggested Constraints". Probamos y vemos que la tabla es correcta, incluso rotando el dispositivo.

> Esta opción de Reset te puede sacar de más de un apuro cuando el ajustar el autolayout sea complicado. Recomiendo probar primero con ella, y si no funciona a la primera entonces ajustar las constraints a mano.

![Celdas constraints](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-1369a27c09c44b1cc2ba0096c479b5118a5bd37b%2Fcell_constraints.png?alt=media)

Si se te queda muy corta la imagen del fondo cuando rotas el dispositivo puedes seleccionar la opción "Scale to Fill" en el inspector de atributos:

![Celdas ajuste del fondo](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-6557097a381761af17e224025abfd385f276de5c%2Fcell_scale.png?alt=media)

#### Insertar datos en la tabla

Para finalizar, extenderemos nuestro programa con las siguientes opciones:

*a)* Vamos a rellenar las celdas de la tabla con datos de libros. Para ello, crearemos una nueva clase (con *File > New > Swift file*) a la que llamaremos `Libro`. Reemplaza en este fichero `import Foundation` por `import UIKit`, y crea la clase con un `String` que será el título del libro, otro `String` que será el autor y una imagen (`UIImage`) que será la portada. Añade también un método `init` para inicializar las variables de la clase.

*b)* Ahora crearemos un array dentro de la clase `TableViewController`, lo inicializamos y lo completamos con los 3 libros que prefieras. Cada elemento del array será de la clase `Libro`.

*c)* Una vez creado el array de libros vamos a mostrarlos en nuestra tabla. Para ello deberemos modificar los métodos de la clase `TableViewController`, asignando los datos del array a las celdas correspondientes.

*d)* Cuando hayamos terminado, comprobamos que la aplicación funciona según lo esperado.

***

### Ejercicio 2- Personalización de ToolBars

Los *ToolBars*, al igual que la mayor parte de componentes, se pueden personalizar para conseguir las funcionalidades que deseemos para nuestras apps. En el siguiente ejemplo vamos a crear una vista *ToolBar* personalizada con una imagen de fondo, estilos para los botones, añadiendo botones de distinto tipo, etc.

Comenzamos creando un nuevo proyecto de tipo *iOS > App* con el nombre `ejercicio_toolbar` y con interfaz *storyboard*.

#### Diseño de la vista

Abrimos la vista principal del *storyboard*, arrastramos un objeto `UIToolBar` y lo situamos en la parte superior. También vamos a añadir un objeto `UILabel` en el centro de la vista.

![ToolBar básico](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-724369cb65fdb7ce6cf627594cf68341bfc0492b%2Ftoolbar_empty.png?alt=media)

Ahora vamos a añadir los elementos que deseemos sobre el *ToolBar*. En nuestro caso arrastraremos desde el listado de objetos (en el siguiente orden) un *Text Field*, un *Flexible Space Bar* y un *Segmented Control*. Borraremos el botón *Item* de la izquierda. El *ToolBar* debe quedar de la siguiente manera:

![ToolBar con botones y campos de texto](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-8a11c09fe6c76c41afffb556b70a3240f602f427%2Ftoolbar_view.png?alt=media)

Como podemos observar, la función del objeto *Flexible Space* no es más que añadir un espacio flexible entre dos objetos dentro de un *ToolBar* o de una barra de navegación.

Una vez tenemos la barra con todos sus elementos, vamos a definir los elementos dentro de la clase. En el código de la clase `ViewController` añadimos los siguientes outlets:

```swift
@IBOutlet weak var toolBar: UIToolbar!
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var segmentedControl: UISegmentedControl!

// Para la etiqueta centrada en la vista
@IBOutlet weak var segmentLabel: UILabel!
```

Seguidamente tenemos que enlazar los *outlets* dentro de la vista. Si ejecutamos el código, es posible que el *toolbar* y el *label* salgan descolocados (o no salgan) cuando rotemos el dispositivo.

#### Ajuste de las constraints

Vamos a añadir *constraints* desde XCode para fijar los márgenes. Selecciona el toolbar, y añade las siguientes *constraints* (arriba, izquierda, derecha) marcando *Constrain to margins* para que no se solape con la barra de estado:

![Toolbar constraints](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-55bd1bad078c38966614d3e1a08343ba9e159573%2Ftoolbar_constraints.png?alt=media)

Vamos a centrar ahora el *label*, añadiendo restricciones de centrado horizontal y vertical:

![Label constraints](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-04aaad7975e1bd772ef7aead7ae626d84d01605a%2Ftoolbar_label_constraints.png?alt=media)

Puedes ver los *constraints* añadidos para cada elemento en el *Size inspector*. Por ejemplo, para el *label* tendríamos:

![Label size inspector](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-1923c4ffadf762de0ade1fc3aa8b9e608fe01024%2Ftoolbar_size_inspector.png?alt=media)

#### Acciones

Ahora vamos a implementar la acción del *Segmented Control*. Para ello declaramos el siguiente método dentro de la clase `ViewController`:

```swift
@IBAction func segmentedControlIndexChanged(_ sender: Any) {
          self.segmentLabel.text = self.segmentedControl.selectedSegmentIndex == 0 ? "Segmento 1" : "Segmento 2"
}
```

Para que el método se invoque cuando se pulsa un botón del *Segmented Control*, deberemos enlazarlo con la vista.

En este momento ya podemos ejecutar el proyecto por primera vez y comprobar que al pulsar sobre uno de los botones del *Segmented Control* la etiqueta *Label* cambia.

#### Personalización avanzada

Ya tenemos un objeto *ToolBar* con una personalización básica funcionando. Ahora vamos a cambiarlo un poco más: vamos a añadirle una imagen de fondo, a asignar un color de fondo al *Segmented Control* y a modificar el diseño del *TextField*. Para hacer todo esto debemos cambiar el método `viewDidLoad` de la clase `ViewController` para que quede de la siguiente manera:

```swift
override func viewDidLoad() {
    super.viewDidLoad()

    // Vamos a añadir la imagen de fondo como un imageView, pero esta vez sin usar el storyboard (por hacerlo distinto)
    let iv = UIImageView(image:UIImage(named:"fondo_madera.png"))
    iv.frame = CGRect(x:0, y:0, width:self.toolBar.frame.size.width, height:44)
    iv.autoresizingMask = UIView.AutoresizingMask.flexibleWidth

    // Añadimos la subview
    self.toolBar.insertSubview(iv, at:0)

    // Cambiamos el color del SegmentedControl
    self.segmentedControl.tintColor=UIColor.brown

    // Personalizamos el TextField desde código (también se podría hacer desde el Interface Builder)
    self.textField.textColor = UIColor.white
    self.textField.borderStyle = UITextField.BorderStyle.none
    self.textField.background = UIImage(named:"fondo_textfield.png")
    self.textField.placeholder = "Escribe aquí"
}
```

Para que el código funcione debemos descargarnos las imágenes desde [aquí](https://github.com/mastermoviles/interfaz-usuario-ios/blob/master/docs/gitbook/assets/imagenes_toolbar.zip) y añadirlas a los *Assets* del proyecto. Una vez hecho esto ya podemos ejecutarlo y ver cómo ha cambiado. De esta forma tendremos nuestro componente *ToolBar* bastante personalizado, dándole a la aplicación un aspecto visual diferenciado.

![ToolBar personalizado](https://4135469700-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LDGNOA4SEPtKxESUKUy%2Fuploads%2Fgit-blob-a223558c3e5c52b93bf0269aace268be3816cc6e%2Ftoolbar_final.png?alt=media)

La metodología que acabamos de seguir también sirve para personalizar los componentes `UITabBar` y `UINavigationBar`, entre otros.
