GO FURTHER, FASTER: Try the Swift Career Accelerator today! >>

Diseñando nuestra interfaz

Nuestra app carga correctamente todas las imágenes de tormentas, pero no hace nada interesante con ellas - imprimir a la consola de Xcode es de ayuda para depurar, ¡pero puedo prometerle que no es una aplicación de las más vendidas!

Para arreglarlo, nuestro próximo objetivo es crear una interfaz que liste las imágenes para que los usuarios puedan seleccionar una. UIKit - el framework de interfaz de usuario de iOS - tiene muchas herramientas de interfaz ya construidas que podemos usar para construir apps poderosas que parezcan y funcionen de la forma que los usuarios esperan.

Para esta app, nuestro componente principal de interfaz se llama UITableViewController. Está basado en UIViewController – el tipo más básico de pantalla de Apple - pero añade la habilidad de mostrar filas de datos que se pueden desplazar y seleccionar. Puede ver UITableViewController en la app de Ajustes, en Mail, en Notas, en Salud y en muchas más - es poderosa, flexible y extremadamente rápida, así que no es ninguna sorpresa que sea usada en muchas apps.

Nuestra pantalla ViewController existente está basada en UIViewController, pero lo que queremos es que esté basada en UITableViewController en su lugar. Esto no toma mucho, pero va a conocer una nueva parte de Xcode llamada Interface Builder.

Iremos a Interface Builder en un momento. Aunque primero necesitamos hacer un pequeño cambio en ViewController.swift. Encuentre esta línea:

class ViewController: UIViewController {

Esta es la línea que dice “crea una nueva pantalla llamada ViewController y haz que se construya sobre la propia pantalla UIViewController de Apple.” Quiero que la cambie a esto:

class ViewController: UITableViewController {

Es sólo una pequeña diferencia, pero es una importante: quiere decir que ViewController ahora hereda su funcionalidad de UITableViewController en lugar de hacerlo de UIViewController, lo que nos da una gran cantidad de funcionalidad extra gratis como verá en un momento.

Entre bastidores, UITableViewController se construye sobre UIViewController - es lo que se llama una “jerarquía de clases”, y es una manera común de reunir funcionalidad rápidamente.

Hemos cambiado el código de ViewController para que se construya sobre UITableViewController, pero necesitamos también cambiar la interfaz de usuario para que coincida. Las interfaces de usuario pueden ser escritas por completo en código si se quiere - y muchos desarrolladores hacen justo eso - pero son creadas más comúnmente usando un editor gráfico llamado Interface Builder. Necesitamos decirle al Interface Builder (comúnmente llamado simplemente "IB") que ViewController es un table view controller, para que coincida con el cambio que acabamos de hacer en nuestro código.

Hasta este punto hemos estado trabajando enteramente en el fichero ViewController.swift, pero ahora me gustaría que usara el navegador del proyecto (el panel a la izquierda) para seleccionar el fichero Main.storyboard. Los Storyboards contienen la interfaz de usuario de su aplicación, y le permite visualizar algunas o todas ellas en una sola ventana.

Cuando selecciona Main.storyboard, cambiará al editor visual del Interface Builder, y verá algo como la imagen de abajo

La plantilla Single View App le da un gran y vacío lienzo sobre el que dibujar.

Este gran espacio en blanco es el que produce el gran espacio en blanco cuando se ejecuta la app. Si suelta nuevos componentes en este espacio, deberían ser visibles cuando la app se ejecuta. De todas formas, no queremos hacer eso - de hecho, no queremos este gran espacio en blanco de ninguna forma, así que vamos a eliminarlo.

La mejor forma de ver, seleccionar, editar y eliminar items en Interface Builder es usar el document outline, pero es posible que esté escondido para usted así que la primera cosa a hacer es mostrarlo. Vaya al menú Editor y elija Show Document Outline - es probablemente la tercera opción comenzando desde arriba. Si ve Hide Document Outline en su lugar, significa que el document outline es ya visible.

El document outline muestra todos los componentes en todas las pantallas en su storyboard. Debería ver ya ahí “View Controller Scene”, así que por favor selecciónelo y presione la tecla borrar en su teclado para eliminarlo.

En lugar del aburrido y antiguo UIViewController, queremos un fantástico y nuevo UITableViewController para que coincida con el cambio que hicimos en nuestro código. Para crear uno, pulse Cmd+Shift+L para mostrar la librería de objetos. De otra forma, si no le gustan los atajos de teclado puede ir al menú View y elegir Libraries > Show Library en su lugar.

La librería de objetos flota sobre la ventana de Xcode, y contiene una selección de componentes gráficos que puede arrastrar fuera y reorganizar según el contenido de su corazón. Contiene muchos componentes, así que encontrará útil introducir unas pocas letras en la caja de texto "Filter" para reducir la selección.

Consejo: Si quiere que la librería de objetos permanezca abierta después de que arrastre algo fuera, use Alt+Cmd+Shift+L y se convertirá en una ventana movible y redimensionable cuando aparezca.

Justo ahora, el componente que queremos se llama Table View Controller. Si teclea "table" en la caja de texto Filter verá Table View Controller, Table View, y Table View Cell. Son todas cosas diferentes, así que esté seguro de que elije Table View Controller - tiene un fondo amarillo en su icono.

Haga click sobre el componente Table View, arrástrelo fuera al gran espacio abierto que existe donde estaba el anterior view controller. Cuando suelte el view controller en el lienzo del storyboard, se transformará en una ventana que se verá como la siguiente:

Una vez que ha eliminado el view controller original y lo ha reemplazado con un nuevo view controller, Xcode debería parecer lo siguiente.

Últimos toques para la interfaz de usuario

Antes de que terminemos aquí, necesitamos hacer unos pequeños cambios.

Primero, necesitamos decirle a Xcode que este table view controller del storyboard es el mismo que tenemos en el código dentro de ViewController.swift. Para hacer esto, pulse Alt+Cmd+3 para activar el identity inspector (o vaya a View > Utilities > Show Identity Inspector), después busque en la parte superior una caja llamada "Class". Esta tendrá “UITableViewController” escrito en ella en texto gris claro, pero si hace click en la flecha a su derecha debería ver una lista desplegable que contiene "ViewController" - por favor, selecciónela ahora.

Segundo, necesitamos decirle a Xcode que este nuevo table view controller es lo que se debería mostrar primero cuando la app se ejecute. Para hacer esto, pulse Alt+Cmd+4 para activar el attibutes inspector (o vaya a View > Utilities > Show Attributes Inspector) y entonces busque el check llamado “Is Initial View Controller” y asegúrese de que está marcado.

Tercero, quiero que use el document outline para mirar dentro del nuevo table view controller. Dentro debería ver que contiene un "Table View", que a su vez contiene "Cell". Un table view cell es el responsable de mostrar una fila de datos en una tabla, y vamos a mostrar un nombre de imagen en cada celda.

Por favor, seleccione entonces "Cell" en el attributes inspector, introduzca el texto "Picture" en campo de texto marcado como Identifier. Mientras está ahí, cambie la opción Style en la parte superior del attributes inspector – debería ser Custom ahora, pero por favor cámbielo a Basic.

Finalmente, vamos a poner el view controller completo dentro de algo más. Es algo que no necesitamos configurar o por lo que preocuparnos, pero es un elemento de interfaz de usuario extremadamente común en iOS y creo que lo reconocerá inmediatamente. Es el llamado navigation controller, y lo ve en acción en apps como Ajustes y Mail - proporciona la barra delgada gris en la parte superior de la pantalla, y es responsable de la animación de desplazamiento derecha-a-izquierda que ocurre cuando se mueve entre pantallas en iOS.

Para situar nuestro table view controller dentro de un navigation controller, todo lo que necesita hacer es ir al menú Editor y elegir Embed In > Navigation Controller. El Interface Builder moverá su view controller a la derecha y añadirá un navigation controller a su alrededor - debería ver una barra gris simulada encima de su table view ahora. También moverá la propiedad “Is Initial View Controller” al navigation controller.

En este punto ha hecho lo suficiente para echar un vistazo a los resultados de su trabajo: pulse el botón de play de Xcode ahora, o pulse Cmd+R si quiere sentirse un poco de la élite. Una vez que su código se ejecuta, verá la caja blanca reemplazada por una larga y vacía table view. Si hace click y mueve su ratón alrededor, verá que se desplaza y rebota tal y como esperaba, a pesar de que obviamente no hay datos aún en ella. Debería también ver una barra de navegación en la parte superior; esto será importante más tarde.

Mostrando muchas filas

El próximo paso es hacer que la table view muestre algunos datos. Específicamente, queremos que muestre la lista de las imágenes "nssl", una por fila. El tipo de datos de Apple UITableViewController proporciona comportamientos por defecto para un montón de cosas, pero por defecto dice que hay cero filas.

Nuestra ventana ViewController se construye sobre UITableViewController y puede sustituir el comportamiento por defecto del table view de Apple para proporcionar personalización donde se necesite. Solo necesita sustituir los que necesite, los valores por defecto son todos razonables.

Para hacer que la tabla muestre nuestras columnas, necesitamos sustituir dos comportamientos: cuántas filas deben ser mostradas, y qué contiene cada fila. Esto es hecho escribiendo dos métodos especialmente nombrados, pero cuando es nuevo en Swift pueden parecer un poco extraños al principio. Para asegurarnos de que todo el mundo pueda seguirnos voy a hacer esto despacio - ¡este es su primer proyecto después de todo!

Comenzamos con el método que establece cuantas filas deben aparecer en la tabla. Añada este código justo después del final de viewDidLoad() - si comienza a escribir "numberof" puede usar la compleción de código de Xcode para hacer la mayoría del trabajo por usted:

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

Nota: tiene que ser después del final de viewDidLoad(), lo que significa después de su corchete de cierre.

Este método contiene la palabra “table view” tres veces, lo que es profundamente confuso de primeras, así que descompongámoslo y veamos qué significa:

  • La palabra override significa que el método ha sido ya definido, y que queremos sustituir el comportamiento existente con este nuevo comportamiento. Si no lo sustituyó, entonces el método anteriormente definido se ejecutaría, y en esta instancia diría que no hay filas.
  • La palabra func comienza una nueva función o un nuevo método; Swift usa la misma palabra para ambos. Técnicamente hablando un método es una función que aparece dentro de una clase, justo como nuestro ViewController, pero de otra manera no hay diferencia.
  • El nombre del método viene a continuación: tableView. Esto no suena muy útil, pero la forma en la que Apple define métodos es para asegurar que la información que es pasada a ellos - los parámetros - es nombrada útilmente, y en este caso la primera cosa que es pasada es el table view que ha desencadenado el código. Un table view, como debería hacer deducido, es la cosa desplazable que contendrá todos nuestros nombres de imágenes, y es un componente central en iOS.
  • Como fue prometido, la siguiente cosa es tableView: UITableView, que es el table view que desencadenó el código. Pero este contiene dos piezas de información a la vez: tableView esel nombre que podemos usar para referenciar el table view dentro del método, y UITableView es el tipo de dato - la parte que describe qué es.
  • La parte más importante del método viene a continuación: numberOfRowsInSection section: Int. Esto describe qué es lo que el método hace. Sabemos que implica a un table view porque es el nombre del método, pero la parte numberOfRowsInSection es la acción: este código será desencadenado cuando iOS quiere saber cuántas filas hay en la table view. La parte section está porque las table views pueden ser partidas en secciones, en la forma en la que la app Contactos separa los nombres por su primera letra. Solo tenemos una sección, así que podemos ignorar este número. La parte Int significa "esto será un entero", que significa un número completo como el número 3, 30 o 35678.
  • Finalmente, -> Int significa "este método debe devolver un entero", que debería ser el número de filas a mostrar en la tabla.

Hay una cosa más que he olvidado, y lo he olvidado por un razón: es un poco confuso en este punto de su carrera en Swift. ¿Se ha dado cuenta dl _ ahí? Esto es un guion bajo. Esto cambia la forma en la que el método es llamado. Para ilustrarlo, aquí tiene una función muy simple:

func doStuff(thing: String) {
    // do stuff with "thing"
}

Está vacía, porque su contenido no importa. En su lugar, focalicémonos en como es llamada. Ahora es llamada así:

doStuff(thing: "Hello")

Necesita escribir el nombre del parámetro thing cuando llama a la función doStuff(). Esto es una característica de Swift, y ayuda a hacer su código más fácil de leer. Algunas veces no tiene realmente sentido tener un nombre para el primer parámetro, usualmente porque está dicho en el nombre del método.

Cuando esto ocurre, use el caracter de guión bajo así: When that happens, you use the underscore character like this:

func doStuff(_ thing: String) {
    // do stuff with "thing"
}

Esto significa "cuando llamo a esta función no quiero escribir thing, pero dentro de la función quiero usar thing para referirme al valor que fue pasado.

Esto es lo que está ocurriendo con nuestro método de table view. El método es nombrado tableView() porque su primer parámetro es el table view con el que está trabajando. No tendría mucho sentido escribir tableView(tableView: someTableView), así que usando el guion bajo significa que podría escribir tableView(someTableView) en su lugar.

No voy a pretender que sea fácil de enteder cómo los métodos de Swift parecen y trabajan, pero la mejor cosa a hacer es no preocuparse mucho si no lo entiende ahora porque después de unas cuantas horas de codificación será instintivo.

Por lo menos necesita saber que estos métodos son referidos usando sus nombres (tableView) y algunos nombres de parámetros. Parámetros sin nombres son simplemente referidos como guiones bajos: _. Así que para darle su nombre completo, el método que acaba de escribir es referido como tableView(_:numberOfRowsInSection:) - torpe, lo sé, razón por la cual la mayoría de la gente suele hablar sobre el trozo importante, por ejemplo, "en el método numberOfRowsInSection"

Escribimos solo una línea de código en el método, que fue return pictures.count. Esto significa "devuelve el número de imágenes en nuestro array," por lo que pedimos que haya tantas filas en la tabla como imágenes.

Desencolando celdas

Este es el primero de los dos métodos que necesitamos escribir para completar esta parte de la app. La segunda es para especificar como debe parecer cada fila, y sigue una convención de nombre similar al del método previo. Añada este código ahora:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Picture", for: indexPath)
    cell.textLabel?.text = pictures[indexPath.row]
    return cell
}

Descompongámoslo en partes otra vez, así podrá ver exactamente como funciona.

Primero, override func tableView(_ tableView: UITableView es idéntico al del anterior método: el nombre del método es solo tableView(), y recibirá una table view como primer parámetro. El _ significa que no tiene que tener un nombre externamente, porque es el mismo que el nombre del método.

Segundo, cellForRowAt indexPath: IndexPath es la parte importante del nombre del método. El método es llamado cellForRowAt, y será llamado cuando necesite proporcionar una fila. La fila para mostrar es especificada en el parámetro indexPath, que es del tipo IndexPath. Este es un tipo de datos que contiene tanto un número de sección como un número de fila. Solo tenemos una sección, así que podemos ignorarlo y usar sólo el número de fila.

Tercero, -> UITableViewCell significa que este método debe devolver una table view cell. Si recuerda, creamos una dentro de Interface Builder y le dimos el identificador "Picture", así que queremos usarlo.

Aquí es donde la magia de iOS tiene lugar: si mira a la app de Ajustes, verá que sólo caben alrededor de 12 filas en la pantalla en un momento dado, dependiendo de la medida de su teléfono.

Para ahorrar tiempo de CPU y RAM, iOS solo crea tantas filas como necesita para trabajar. Cuando una fila se mueve fuera de la parte superior de la pantalla, iOS la pondrá en una cola de reutilización lista para ser reciclada en una nueva fila que viene desde la parte inferior. Esto significa que puede desplazarse a través de cientos de filas en un segundo, e iOS puede comportarse perezosamente y evitar crear ninguna nueva table view cell - solo recicla las existentes.

Esta funcionalidad es cocinada dentro de iOS, y es exactamente lo que nuestro código hace en esta línea:

let cell = tableView.dequeueReusableCell(withIdentifier: "Picture", for: indexPath)

Esto crea una nueva constante llamada cell desencolando una cell reciclada de la tabla. Tenemos que darle un identificador del tipo de celda que queremos reciclar, para eso introducimos el mismo nombre que dimos a Interface Builder: "Picture". También pasamos el índice que ha sido requerido; esto se usa internamente por el table view.

Esto nos devolverá una table view cell con la que podemos trabajar para mostrar información. Puede crearse su propio diseño de table view cell si quiere (¡más sobre esto más adelante!), pero estamos usando el estilo básico incorporado que tiene una etiqueta de texto. Ahora es cuando la línea dos entra: le da a la text label de la cell el mismo texto que una imagen de nuestro array. Aquí va el código de nuevo:

cell.textLabel?.text = pictures[indexPath.row]

El cell tiene una propiedad llamada textLabel, pero es opcional: puede ser un text label o puede no serlo - si ha diseñado la suya, por ejemplo. En lugar de escribir comprobaciones para ver si hay una text label o no, Swift nos deja usar un signo de interrogación - textLabel? – para decir "haz esto solo si hay una text label y no hagas nada en otro caso."

Queremos poner el label texto al mismo nombre de la imagen correcta de nuestro array pictures, y eso es exactamente lo que el código hace. indexPath.row contendrá el número de fila que nos están pidiendo cargar, así que vamos a usarlo para leer la correspondiente imagen de pictures, y ponerla en el text label de la cell.

La última línea en el método es return cell. Recuerde, este método espera que se devuelva una view cell, así que necesitamos devolver la que hemos creado - esto es lo que return cell hace.

Con estos dos pequeños métodos puestos, puede ejecutar su código de nuevo ahora y ver cómo parece. Si todo va bien debería ver 10 table view cells, cada una con un nombre de imagen diferente dentro. Si hace click en una de ellas se volverá gris, pero nada más ocurrirá. Arreglémoslo ahora...

Hacking with Swift is sponsored by Proxyman.

SPONSORED Debug 10x faster with Proxyman. Your ultimate tool to capture HTTPs requests/ responses, natively built for iPhone and macOS. You’d be surprised how much you can learn about any system by watching what it does over the network.

Try Now!

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.