Trabajar con Controladores de vista de tabla en Swift

Escrito por Reinder de Vries el 3 de agosto de 2020 en Desarrollo de aplicaciones, iOS

Trabajar con Controladores de vista de tabla en Swift

En este tutorial, le mostraré paso a paso cómo funcionan los controladores de vista de tabla y cómo puede usarlos. Entraremos en la gama completa de UITableViewController, sumergiéndonos en la Programación Orientada a objetos, la delegación y los mecanismos detrás de escena de las vistas de mesa.

Un controlador de vista de tabla muestra información estructurada y repetible en una lista vertical. Utiliza la clase UITableViewController en tu aplicación iOS para crear un controlador de vista de tabla.

Trabajar con un controlador de vista de tabla también significa trabajar con algunos conceptos importantes de desarrollo de iOS, como la subclase, el patrón de diseño de delegación y la reutilización de vistas.

Es importante para los desarrolladores profesionales y prácticos de iOS (¡tú!) para dominar el trabajo con controladores de vista de tabla. Una vez que te hayas acostumbrado a trabajar en un componente de interfaz de usuario multifacético, como UITableViewController, otros aspectos más complejos del desarrollo de iOS también comenzarán a tener sentido.

Listo? ¡Vamos a sumergirnos!

  1. Cómo Funciona un Controlador de Vista de Tabla
  2. Configurar un Controlador de Vista de Tabla Simple
  3. Codificar La Fuente de Datos del Controlador de Vista de Tabla
  4. Proporcionar Celdas al Controlador de Vista de Tabla
  5. Responder a la Interacción del Usuario
  6. View Controller Funciona

    Si ha utilizado alguna aplicación iOS anteriormente, ha utilizado controladores de vista de tabla anteriormente. ¡Se usan con frecuencia en aplicaciones iOS!

    Este es un ejemplo de controlador de vista de tabla:

     Controlador de vista de tabla ejemplo

    Un controlador de vista de tabla normalmente tiene estos componentes visibles:

    • Una vista de tabla, que es el componente de interfaz de usuario, o vista, que se muestra en pantalla. Una vista de tabla es una instancia de la clase UITableView, que es una subclase de UIScrollView.
    • Celdas de vista de tabla, que son las filas o vistas repetibles que se muestran en la vista de tabla. Una celda de vista de tabla es una instancia de una clase UITableViewCell, y esa clase a menudo se subclase para crear celdas de vista de tabla personalizadas.

    Un controlador de vista de tabla también se basa en el uso de estos componentes, entre bastidores:

    • Un delegado de vista de tabla, que es responsable de administrar el diseño de la vista de tabla y responder a los eventos de interacción del usuario. Un delegado de vista de tabla es una instancia de la clase UITableViewDelegate.
    • Una fuente de datos de vista de tabla, que es responsable de administrar los datos de una vista de tabla, incluidas las celdas y secciones de la vista de tabla. Una fuente de datos es una instancia de la clase UITableViewDataSource.

    Un controlador de navegación se utiliza a menudo en conjunción con un controlador de vista de tabla para permitir la navegación entre la vista de tabla y los controladores de vista posteriores, y para mostrar una barra de navegación por encima de la vista de tabla.

    ¡La parte más interesante de trabajar con controladores de vista de tabla es el controlador de vista de tabla en sí! ¿Cómo es eso?

    ¿Está familiarizado con la arquitectura Modelo-Vista-Controlador? Según la arquitectura Modelo-Vista-Controlador, una vista de tabla y una celda de vista de tabla son vistas, y un controlador de vista de tabla es un controlador.

    Las vistas son responsables de mostrar la información de forma visible al usuario, con una interfaz de usuario (UI). Los controladores son responsables de implementar la lógica, administrar los datos y tomar decisiones. Dicho de otra manera: no puedes ver un controlador, pero está ahí, gestionando lo que ves a través de las vistas.

    Cuando usas un controlador de vista de tabla en tu app, subclases la clase UITableViewController. La clase UITableViewController en sí es una subclase de UIViewController.

    Aquí está la jerarquía de clases de un controlador de vista de tabla de ejemplo que muestra información de contacto:

    • una instancia de ContactsTableViewController
      • subclases UITableViewController
        • subclases UIViewController

    Según los principios de Programación Orientada a Objetos (OOP), cuando clase RaceCar subclases clase Vehicle, hereda las propiedades y funciones de esa superclase, como maxSpeed y drive().

    La clase Vehicle se denomina superclase de RaceCar. Este principio se llama herencia.

    Confuso? Puede ser! Piénsalo así.: para que su controlador de vista de tabla funcione correctamente, necesitará heredar un montón de código, para que no tenga que escribir todo ese código usted mismo. La jerarquía de clases, y OOP, está ahí para estructurar esa herencia.

     Diagrama de clases del controlador de vista de tabla

    Puede trabajar con vistas de tabla sin usar un controlador de vista de tabla. Simplemente agregue un UITableView a un controlador de vista, proporciónele implementaciones de las funciones de delegado de vista de tabla y fuente de datos, y listo.

    La clase UITableViewController proporciona implementaciones predeterminadas de estas funciones de delegado de vista de tabla y fuente de datos de vista de tabla. ¡Ese es un aspecto crucial de trabajar con un controlador de vista de mesa!

    Como verá en los siguientes capítulos de este tutorial, reemplazaremos estas funciones con nuestras propias implementaciones. Podemos personalizar el controlador de vista de tabla haciendo eso.

    Aprende a crear aplicaciones iOS

    Empieza con iOS 14 y Swift 5

    Inscríbete en mi curso de desarrollo de iOS y aprende a crear excelentes aplicaciones iOS 14 con Swift 5 y Xcode 12.

    Configurar un Controlador de Vista de tabla simple

    Muy bien, pongamos todo eso en práctica. En esta sección, vamos a construir un controlador de vista de tabla simple. Van a implementar las funciones necesarias para que funcione, y les explicaré cómo funcionan a medida que avanzamos. Vamos a salir!

    Puede usar 3 enfoques diferentes para trabajar con interfaces de usuario en Xcode:

    1. Crear vistas mediante programación, es decir, codificarlas manualmente
    2. Configurar interfaces de usuario en el Creador de interfaces y conectarlas con código Swift a través de XIBs
    3. Configurar interfaces de usuario y sus transiciones en el creador de interfaces mediante guiones gráficos
    4. (Técnicamente, también puede usar SwiftUI, pero en cuanto a los controladores de vista de tabla, eso está más allá del alcance de este tutorial.)

    Es tedioso e improductivo codificar UIs a mano. Si no tienes cuidado con los guiones gráficos, terminan ocultando la complejidad del desarrollador. Vamos a trabajar con guiones gráficos, mientras averiguamos exactamente cómo funcionan detrás de escena.

    Esto es lo que vas a hacer:

    Primero, crea un nuevo proyecto Xcode a través de Archivo → Nuevo → Proyecto…. Asegúrese de elegir la plantilla de la aplicación y elija Storyboard para Interfaz y Delegado de la aplicación UIKit para Ciclo de vida.

    A continuación, siga estos pasos:

    1. Haga clic con el botón derecho en su proyecto en el Navegador de proyectos y elija Nuevo archivo
    2. Elija la plantilla de clase Cocoa Touch (iOS)
    3. Elija UITableViewController para la Subclase de
    4. Nombre de la clase ContactsTableViewController
    5. , haga clic en Siguiente y guarde el archivo junto con los otros archivos Swift

    Por último, haga esto:

    1. Vaya a Main.storyboard y elimine la Escena del Controlador de vista existente
    2. Agregue un nuevo Controlador de vista de tabla al guion gráfico a través de la Biblioteca
    3. Con el controlador de vista de tabla seleccionado, vaya al Inspector de atributos y marque la casilla de verificación Es Controlador de vista inicial
    4. ContactsTableViewController

    ¡Eso es! Ahora tiene un controlador de vista de tabla dentro del guion gráfico del proyecto y lo ha conectado a la clase Swift ContactsTableViewController.

     Configuración del controlador de vista de tabla Xcode

    Como ha adivinado, su clase ContactsTableViewController es una subclase de UITableViewController. Puede verlo en el archivo Swift, en la parte superior, en la declaración de clase.

    class ContactsTableViewController: UITableViewController { ···

    Esta sintaxis significa: la clase ContactsTableViewController es una subclase de UITableViewController.

    Cuando hace clic con el botón derecho en «UITableViewController» mientras mantiene pulsada la tecla Opción, puede ver en la declaración de clase que UITableViewController es una subclase de UIViewController, y se ajusta a los protocolos UITableViewDelegate y UITableViewDataSource.

     Consulte la documentación del Controlador de vista de tabla en Xcode

    ¡Esa es la potencia del controlador de vista de tabla! No solo nos da los componentes individuales para hacer una vista de tabla, el controlador también proporciona una implementación predeterminada. Es por eso que subclase UITableViewController, en lugar de crear un controlador de vista vacío con una vista de tabla.

    En este punto, puede ejecutar su aplicación con Command-R o el botón Reproducir, y ver el controlador de vista de mesa vacía aparecer en la pantalla.

    ¿Por qué, por cierto? ¡Aún no hemos codificado nada! Esto se debe a que el controlador de vista de tabla tiene una implementación predeterminada, que solo muestra celdas vacías en la pantalla. ¡Genial!

     Ejemplo de controlador de vista de tabla en el simulador de iPhone

    Dependiendo de tu versión de Xcode, hemos utilizado el delegado de escena para configurar el controlador de vista inicial de tu proyecto de aplicación. Más información aquí: El Delegado de escena en Xcode

    Un XIB y un PLUMÍN son básicamente lo mismo: contienen información de diseño. Un XIB tiene un formato XML, mientras que un PLUMÍN tiene un formato binario. El XML se compila en binario cuando compilas tu aplicación, por lo que las funciones de UIKit siempre hablan de un «plumín», mientras que Xcode siempre lo llama «xib».

    Codificar la Fuente de datos del Controlador de Vista de tabla

    Ahora que su controlador de vista de tabla se ha configurado, ¡démosle vida! En este capítulo, nos centraremos en las diferentes funciones que necesitará implementar para que su controlador de vista de tabla funcione.

    Como se explicó anteriormente, estas funciones pertenecen al delegado del controlador de vista de tabla o al origen de datos del controlador de vista de tabla. Ambos protocolos utilizan la delegación para personalizar la vista de tabla.

    Las funciones más importantes de UITableViewDataSource son:

    • numberOfSections(in:)
    • tableView(_:numberOfRowsInSection:)
    • tableView(_:cellForRowAt:)

    Otras funciones relevantes para UITableViewDelegate son:

    • tableView(_:didSelectRowAt:)
    • tableView(_:willDisplay:for:)

    Usted puede encontrar más funciones en la Documentación para Desarrolladores de Apple para UITableViewDelegate y UITableViewDataSource.

    Agregar los Datos de contactos

    Comenzará agregando los datos de información de contacto para el controlador de vista de tabla. Agregue la siguiente propiedad a la clase ContactsTableViewController :

    let contacts:] = , , , , , , , , , , , , , , , , ]

    Es una agenda que a todos nos gustaría tener, ¿verdad? Así es como funciona:

    • let contacts declara una constante con nombre contacts. La ha agregado como una propiedad a la clase, por lo que cada instancia de clase tiene acceso a esta constante en todo el código de la clase.
    • El tipo de contacts es ], que es una matriz de matrices de cadenas. Esencialmente estás creando una matriz, de la cual los elementos son matrices de cadenas. (El nombre de una variable y su tipo están separados por dos puntos :)
    • El código = asigna un array literal a contacts, rellenado con los nombres y números de teléfono de algunos multimillonarios.

    En un momento posterior, podemos usar el número de elementos en la matriz con contacts.count. Y podemos obtener nombres y números de teléfono individuales con contacts y contacts, utilizando sintaxis de subíndice.

    Registro de una clase de celda de vista de tabla

    Antes de poder usar celdas en un controlador de vista de tabla, deberá registrarlas con la vista de tabla. Puedes hacerlo de dos maneras:

    1. Al proporcionar una clase de celda de vista de tabla y un identificador
    2. Al proporcionar una celda de vista de tabla XIB y un identificador

    Cuando está utilizando una celda de vista de tabla personalizada, lo más probable es que desee registrar una XIB para eso. Cuando utiliza las celdas de vista de tabla predeterminadas o alguna otra celda programática, registra la clase. ¡Usaremos la clase, por ahora!

    Agregue el siguiente código a la función viewDidLoad() :

    tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellIdentifier")

    Asegúrese de agregarlo debajo de la línea super.viewDidLoad(). Como probablemente sepa, la función viewDidLoad() es parte del ciclo de vida del controlador de vista y pertenece a la clase UIViewController.

    Está sobrescribiendo la función viewDidLoad() para responder a este evento en el ciclo de vida de un controlador de vista, de modo que pueda configurar su vista después de que se haya cargado. En nuestro caso, estamos utilizando la función para registrar la celda de vista de tabla.

    Cuando registra una celda de vista de tabla, también debe proporcionar un identificador. Esto es simplemente para asociar la clase de la celda con un nombre que puede usar más tarde, al desescalar la celda en tableView(_:cellForRowAt:).

    ¿Sigues conmigo? ¡Sigamos adelante!

    Implementando » numberOfSections (in:)»

    La primera función delegada que vamos a implementar es numberOfSections(in:).

    Una vista de tabla puede tener varias secciones o grupos. Cada grupo tiene un encabezado que flota en la parte superior de la fila vertical de celdas. En una aplicación de contactos, puedes agrupar contactos en orden alfabético. En realidad, esto se hace en la aplicación Contactos en el iPhone, donde los contactos se agrupan de la A a la Z.

    La aplicación que estamos creando tiene solo una sección. Agregue la siguiente función a la clase:

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

    Simple, ¿verdad? La función devuelve 1 cuando se llama.

    Implementando » tableView ( _ : numberOfRowsInSection:)»

    Una función similar es tableView(_:numberOfRowsInSection:). En lugar de proporcionar el número de secciones, proporciona el número de filas de una sección. Dado que una vista de tabla muestra celdas en una lista vertical, cada celda corresponde a una fila en la vista de tabla.

    La aplicación que estamos creando tiene solo una sección, y esa sección tiene un número de elementos igual al número de elementos de la matriz contacts. Por lo tanto, eso es contacts.count!

    Agregue la siguiente función a la clase:

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

    Vea cómo funciona? Simplemente devolvemos contacts.count. Si agregara otro nombre y número de teléfono a contacts, también se mostraría muy bien en la vista de tabla.

    Entender filas y secciones

    Nuestra aplicación Contactos es unidimensional, solo muestra una lista de nombres y números de teléfono, y no usa grupos. Pero, ¿qué pasa si tienes una vista de tabla agrupada?

    En la mayoría de los casos, su fuente de datos, como el array contacts, también sería multidimensional. Organizarías grupos en el primer nivel, y elementos individuales en el segundo nivel, «debajo» de los grupos.

    - Countries - A - Afghanistan - Albania - ... - B - Bahamas - Bahrain - ... - C - Cambodia - Cameroon - ...

    El número de grupos es igual a countries.count, y el número de países en un solo grupo es igual a countries.count, donde x es el índice de sección. Ese índice de sección se proporciona como parámetro en tableView(_:numberOfRowsInSection:).

    ¿Notó cómo estas dos funciones tienen un parámetro llamado tableView? Eso es parte del principio de Programación Orientada a Objetos. Técnicamente, puede usar una fuente de datos de vista de tabla y delegar para personalizar varias vistas de tabla. Usaría tableView para identificar con qué vista de tabla está trabajando.

    Imagine que tiene una aplicación de contactos que puede mostrar números de teléfono por nombre o números de teléfono organizados por empresa. Podría implementarlo de varias maneras, por ejemplo, reutilizando los controladores de vista de tabla. ¿O qué pasa si quieres reutilizar el diseño de la aplicación Contactos para mostrar información similar, como restaurantes, lugares o nombres de usuario de Skype? ¡Ahí es donde entra en juego la reutilización de código con OOP!

    Proporcionar celdas al Controlador de Vista de tabla

    ¡Estamos llegando! Pasemos a la función más importante de un controlador de vista de tabla: tableView(_:cellForRowAt:).

    Implementaremos la función antes de profundizar en los detalles, pero hay un par de cosas que debe comprender al respecto:

    • Cuando se llama
    • Qué es una ruta de índice
    • Cómo reutiliza las celdas

    Primero, agregue la siguiente función a la clase ContactsTableViewController :

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{}

    Así es como funciona:

    • La función anula su implementación de superclase desde UITableViewController. Ya sabes cómo funciona, ¿verdad? Estamos anulando la implementación predeterminada y sustituyendo la nuestra. Esto se debe a que UITableViewController ya ha implementado el delegado de vista de tabla y la fuente de datos para nosotros.
    • Al igual que antes, la función tiene un parámetro tableView que podemos usar para identificar la vista de tabla en la que se llama a esta función.
    • Otro parámetro es indexPath, con etiqueta de argumento cellForRowAt. La ruta del índice identifica los índices row y section de la celda. Más sobre eso más adelante.
    • El tipo de retorno de la función es UITableViewCell. Oye, eso es interesante. Esta función es llamada por el controlador de vista de tabla, cada vez que necesitamos proporcionar una celda de vista de tabla.

    Cuando se desplaza por los contactos en esta aplicación, cada vez que se necesita mostrar una celda en la pantalla, se llama a la función tableView(_:cellForRowAt:). ¡Siempre! Te lo demostraré en un momento.

    A continuación, escribamos el cuerpo de la función. Agregue el siguiente código dentro de la función:

    let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier", for: indexPath)print("\(#function) --- section = \(indexPath.section), row = \(indexPath.row)")cell.textLabel?.text = contactsreturn cell

    Esto es lo que pasa:

    • Primero, eliminamos una celda con un identificador. Es exactamente el mismo identificador que usamos antes, al registrar la celda. De esta manera, la vista de tabla sabe qué tipo de celda queremos. La celda desescalada se asigna a la constante cell. Ahora tenemos una celda de vista de tabla con la que trabajar. Más sobre el desagüe más adelante.
    • Luego, imprimimos cierta información en la Consola. Esto es para que podamos ver cuándo se llama a esta función, cuándo se ejecuta la aplicación.
    • Luego, asignamos el nombre del contacto a la etiqueta de texto de esta celda de vista de tabla. El contacts contiene el valor del nombre del contacto, al que llegamos usando indexPath.row. Cada instancia de UITableViewCell tiene una propiedad textLabel de UILabel, y cada etiqueta de ese tipo tiene una propiedad text. Se usa para establecer el texto en la etiqueta.

    No te preocupes, repasaremos cada una de estas cosas con más detalle. Primero, mira si puedes ejecutar tu aplicación. ¿Ves los nombres de los contactos? ¿Ves la salida de depuración en la Consola? ¡Prueba a desplazarte por la aplicación!

     Filas de controladores de vista de tabla

    ¿Cuándo se llama a «tableView(_:cellForRowAt:)»?

    Si ejecutó la aplicación Contactos y jugó con el desplazamiento hacia arriba y hacia abajo, no puede evitar notar que cada vez que se desplaza, la salida de depuración aparece en la Consola.

    Cada vez que aparece una celda que no estaba en la pantalla antes, se llama a la función tableView(_:cellForRowAt:) y aparece una nueva línea en la Consola.

    Entonces, ¿cuándo se llama tableView(_:cellForRowAt:)? ¡Cada vez que se necesita mostrar una celda de vista de tabla en la pantalla!

    El controlador de vista de tabla ha determinado que se necesita una celda, por lo que llama a tableView(_:cellForRowAt:). Nuestra implementación de esa función desactiva una celda, la cambia y la devuelve al controlador de vista de tabla. El controlador de vista de tabla y el marco de trabajo UIKit, luego lo representan gráficamente en la pantalla.

    ¿Qué es una ruta de índice?

    Cada vez que el controlador de vista de tabla necesita una celda de tableView(_:cellForRowAt:), proporciona una ruta de índice como argumento para la función. Dentro del cuerpo de la función, puede usar el parámetro indexPath para saber exactamente qué celda necesita el controlador de vista de tabla.

    Una ruta de índice es como una dirección o una coordenada en una cuadrícula. Un gráfico típico tiene un eje X y un eje Y, por lo que podría expresar una coordenada en ese gráfico como x, y, como 0, 1 y 42, 3. Del mismo modo, una hoja de cálculo tiene filas y columnas con índices.

    Una vista de tabla utiliza secciones y filas. Como se mencionó anteriormente, puede usar secciones para agrupar celdas. Nuestra aplicación solo tiene una sección, y tiene filas contacts.count. Las filas de la vista de tabla se extienden de arriba a abajo.

    Dicho de otra manera: las secciones y filas de una vista de tabla son lo que son las columnas y filas de una hoja de cálculo. Una ruta de índice define una ubicación en la vista de tabla, mediante el uso de una fila y una sección.

    Las filas y secciones están representadas por números, llamados índices. Estos índices comienzan en cero, por lo que la primera fila y sección tendrán un número de índice 0.

    Cuando miras la captura de pantalla anterior, tiene mucho más sentido. La primera celda tiene una ruta de índice 0, 0, la segunda celda 0, 1, continuando hasta la última celda visible con ruta de índice 0, 11.

    El mecanismo de reutilización de la vista de tabla

    Lo más notable de la vista de tabla es su mecanismo para la reutilización de celdas. Es bastante simple, en realidad.

    • Cada vez que un controlador de vista de tabla necesita mostrar una celda en la pantalla, se llama a la función tableView(_:cellForRowAt:), como hemos discutido anteriormente.
    • En lugar de crear una nueva celda de vista de tabla cada vez que se llama a esa función, selecciona una celda creada previamente de una cola.
    • La celda se restablece a un estado vacío, borra su apariencia y la celda se personaliza nuevamente en tableView(_:cellForRowAt:).
    • Cuando una celda se desplaza fuera de la pantalla, no se destruye. Se agrega a la cola, esperando a ser reutilizado.

    Es bastante inteligente, ¿verdad? En lugar de crear y eliminar celdas, simplemente las reutiliza. Pero why ¿por qué?

    Es mucho menos intensivo en memoria reutilizar celdas. El controlador de vista de tabla escribía constantemente en la memoria al crear y eliminar celdas. La gestión de la memoria también sería más intensiva. Cuando las células se reutilizan, la memoria se utiliza de manera más eficiente y se necesitan menos operaciones de memoria.

    Además, itt requiere un poco menos de CPU para reutilizar celdas en lugar de crearlas y eliminarlas, porque simplemente hay menos operaciones involucradas en la reutilización, en comparación con la creación y eliminación de celdas.

    Cuando se desplaza rápidamente por una vista de tabla, no ve celdas nuevas, sino que ve las mismas celdas una y otra vez, con nueva información.

    El código involucrado con la reutilización de celdas es este:

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

    La función dequeueReusableCell(withIdentifier:) intenta desescalar una celda. Cuando no hay celdas en la cola, se creará una celda para nosotros. El identificador se utiliza para mantener cada tipo de celda en su propia cola y para asegurarse de que se utiliza la clase correcta para crear celdas nuevas.

    Aprende a crear aplicaciones iOS

    Empieza con iOS 14 y Swift 5

    Inscríbete en mi curso de desarrollo de iOS y aprende a crear excelentes aplicaciones iOS 14 con Swift 5 y Xcode 12.

    Responder a la interacción del usuario

    Una cosa falta en nuestro controlador de vista de tabla: ¡la capacidad de llamar a las personas de nuestra lista de contactos! Pero antes de hacer eso, asegurémonos de que también pueda ver el número de teléfono de un contacto en el controlador de vista de tabla.

    La clase predeterminada UITableViewCell tiene 4 tipos diferentes, como se expresa en la enumeración UITableViewCellStyle. Puedes elegir entre:

    • .default – una vista sencilla con una línea de texto negro
    • .value1 – una vista simple con una línea de texto negro a la izquierda y una pequeña etiqueta azul a la derecha (utilizada en la aplicación Configuración)
    • .value2 – una vista sencilla con una línea de texto negro a la derecha y una pequeña etiqueta azul a la izquierda (utilizada en la aplicación Contactos)
    • .subtitle – una vista simple con una línea de texto negro y una línea más pequeña de texto gris debajo de ella

    La mayoría de los desarrolladores usan celdas de vista de tabla personalizadas en estos días, por lo que no verá estos tipos de celdas con tanta frecuencia. Pero están ahí!

    Tenemos que ajustar ligeramente el código en tableView(_:cellForRowAt:). Reemplace la primera línea de la función con el siguiente código:

    var cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier")if cell == nil{ cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cellIdentifier")}

    Si miras de cerca, verás que hemos eliminado la parte for: indexPath de la llamada dequeueReusableCell(...). En su lugar, esa función ahora devuelve una opción. Cuando no puede desescalar una celda, la función devuelve nil.

    Luego saltamos nosotros mismos para crear la celda, si es nil. Eso se ve en la segunda parte del código. Se utiliza una instrucción condicional if para comprobar si cell es igual a nil, y si es cierto, se crea la celda con el inicializador UITableViewCell(style:reuseIdentifier:).

    Ese inicializador obtiene dos argumentos, el estilo de celda .subtitle y el identificador que usamos anteriormente.

    En este punto tenemos un problema, porque cell ahora es opcional. Su tipo es UITableViewCell?, pero el tipo de retorno de función exige que devolvamos una instancia con un tipo no opcional UITableViewCell.

    Afortunadamente, esta es una de esas instancias en las que podemos usar forzar el desenvolver de forma segura para desenvolver el valor opcional. Debido a la forma en que está escrito nuestro código, es imposible que cell sea nil más allá del condicional. Puede garantizar que cell no es nil después de la declaración if.

    Asegúrese de actualizar la función para usar desenrollado forzado para cell. Además, agregue la siguiente línea de código debajo de cell!.textLabel ... para configurar el subtítulo de la celda para mostrar el número de teléfono del contacto.

    cell!.detailTextLabel?.text = contacts

    Toda la función ahora se ve así:

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{ var cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier") if cell == nil { cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cellIdentifier") } print("\(#function) --- section = \(indexPath.section), row = \(indexPath.row)") cell!.textLabel?.text = contacts cell!.detailTextLabel?.text = contacts return cell!}

    Por último, asegúrese de eliminar la siguiente línea de viewDidLoad(). Evitará que la vista de tabla inicialice celdas con el tipo incorrecto.

    tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellIdentifier")

    ¡Muy bien! Ejecute su aplicación con Comando + R o el botón Reproducir y compruebe si funciona. ¿Ves nombres y números de teléfono? ¡Bien!

    Luego, para la pieza de resistencia, agreguemos esa función de interacción con el usuario. Ahora que ha aprendido las complejidades del controlador de vista de tabla, creo que ya sabe cómo funciona esta siguiente función.

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){ if let url = URL(string: "tel://" + contacts) { UIApplication.shared.open(url) }}

    De nuevo, estamos anulando la implementación predeterminada de la función tableView(_:didSelectRowAt:). Esta función se llama cuando un usuario toca la celda de una vista de tabla y pertenece al protocolo UITableViewDelegate. Al igual que las otras funciones, proporciona la ruta de índice de la celda que se toca.

    En el cuerpo de la función, simplemente estamos creando una URL tel:// a partir del número de teléfono. Luego le decimos a la aplicación que abra esa URL, lo que le indica a iOS que inicie una llamada a este número. Ese código solo está ahí con fines ilustrativos. Ten en cuenta que esto no funciona en el Simulador de iPhone y que los números de nuestra aplicación son falsos. (¡Aún así, usarías este código si estás haciendo una aplicación de contactos real!)

    Puede agregar el siguiente código a la función si desea comprobar si funciona correctamente.

    print("\(#function) --- Calling: \(contacts)")

    Esto imprimirá un mensaje de depuración cuando toque la celda de la vista de tabla.

    Más información

    ¡Y eso es todo! Ha sido todo un viaje, pero ahora ya sabes cómo funciona un controlador de vista de mesa.