Students Save 30%! Learn & create with unlimited courses & creative assets Students Save 30%! Save Now
Advertisement
  1. Code
  2. iOS SDK
Code

Bloques y celdas de vista en tablas en iOS

by
Difficulty:BeginnerLength:MediumLanguages:

Spanish (Español) translation by CYC (you can also view the original English article)


Una celda no sabe sobre la vista de tabla a la que pertenece y está bien. De hecho, así es como debería ser. Sin embargo, las personas que son nuevas en este concepto a menudo se confunden. Por ejemplo, si el usuario toca un botón en una tabla de celda de vista, ¿cómo se obtiene la ruta de índice de la celda para que pueda obtener el modelo correspondiente? En este tutorial, te mostraré cómo no hacer esto, cómo se hace generalmente y cómo hacerlo con estilo y elegancia.

1. Introducción

Cuando el usuario toca una celda de vista en la tabla, la vista de la tabla invoca tableView:didSelectRowAtIndexPath: del protocolo UITableViewDelegate en el delegado de la vista de la tabla. Este método acepta dos argumentos, la vista de la tabla y la ruta de índice de la celda que se seleccionó.

Sin embargo, el problema que vamos a abordar en este tutorial es un poco más complejo. Supongamos que tenemos una vista de tabla con celdas, con cada celda que contiene un botón. Cuando se toca el botón, se desencadena una acción. En la acción, necesitamos buscar el modelo que corresponde con la posición de la celda en la vista de tabla. En otras palabras, necesitamos saber la ruta del índice de la celda. ¿Cómo inferimos la ruta del índice de la celda si solo obtenemos una referencia al botón que se pulsó? Ese es el problema que resolveremos en este tutorial.

2. Configuración del proyecto

Paso 1: Crear el proyecto

Crea un nuevo proyecto en Xcode seleccionando la plantilla de aplicación de vista única en la lista de plantillas de aplicaciones para iOS. Nombra el proyecto como Blocks and Cells, configura Dispositivos en iPhone y haz clic en Siguiente. Dile a Xcode dónde quieres guardar el proyecto y presiona Crear.

Select Project Template
Configure Project

Paso 2: Actualizar el objetivo de implementación

Abre el Navegador de proyectos a la izquierda, selecciona el proyecto en la sección Proyecto y establece el Objetivo de implementación en iOS 6. Hacemos esto para asegurarnos de que podemos ejecutar la aplicación tanto en iOS 6 como en iOS 7. La razón de esto será aclarada más adelante en este tutorial.

Configure Project Settings

Paso 3: Crea la subclase UITableViewCell

Selecciona Nuevo > Archivo... en el menú Archivo y elige la clase Objective-C de la lista de plantillas Cocoa Touch. Denomina la clase TPSButtonCell y asegúrate de que hereda de UITableViewCell.

Create UITableViewCell Subclass
Create UITableViewCell Subclass

Abre el archivo de encabezado de la clase y declara dos puntos de salida, una instancia de UILabel llamada titleLabel y una instancia de UIButton llamada actionButton.

Paso 4: Actualizar el Controlador de vista

Abre el archivo de encabezado de la clase TPSViewController y crea una salida denominada tableView de tipo UITableView. El TPSViewController también necesita adoptar los protocolos UITableViewDataSource y UITableViewDelegate.

También necesitamos echar un vistazo breve al archivo de implementación del controlador de vista. Abre TPSViewController.m y declara una variable estática de tipo NSString que usaremos como identificador de reutilización para las celdas en la vista de tabla.

Paso 5: Interfaz de usuario

Abre el archivo Main.storyboard del proyecto, Main.Storyboard, y arrastra una vista de tabla a la vista del controlador de vista. Selecciona la vista de tabla y conecta el dataSource y delegate las salidas con la instancia del controlador de vista. Con la vista de tabla todavía seleccionada, abre el Atributes Inspector o inspector de atributos y establece Prototype Cell en 1. El atributo Content debe configurarse en Dynamic Prototypes. Ahora deberías ver un prototipo de celda en la vista de tabla.

Selecciona la celda del prototipo y establece tu Clase en TPSButtonCell en el Inspector de identidad. Con la celda aún seleccionada, abre el Inspector de atributos y establece el atributo Style en Custom y el Identificador por CellIdentifier.

Arrastra una instancia de UILabel desde la Biblioteca de objetos a la vista de contenido de la celda y repite este paso para una instancia de UIButton. Selecciona la celda, abre el Inspector de conexiones y conecta las etiquetas titleLabel y actionButton con sus contrapartes en la celda del prototipo.

Setting Up the User Interface

Antes de volver a sumergirnos en el código, debemos hacer una conexión más. Selecciona el controlador de vista, abre el Inspector de conexiones una vez más y conecta la salida tableView del controlador de vista con la vista de tabla en el Main.storyboard. Eso es todo por la interfaz de usuario.

3. Llenando la vista de tabla

Paso 1: Crea una fuente de datos

Llenemos la vista de tabla con algunas películas notables que se lanzaron en 2013. En la clase TPS ViewController, declara una propiedad de tipo NSArray y asígnale el nombre dataSource. La variable de instancia correspondiente contendrá las películas que mostraremos en la vista de tabla. Rellenar dataSource con aproximadamente una docena de películas en el método viewDidLoad del controlador de vista.

Paso 2: Implementar el protocolo UITableViewDataSource

La implementación del protocolo UITableViewDataSource es muy fácil. Solo necesitamos implementar numberOfSectionsInTableView :, tableView: numberOfRowsInSection :, y tableView: cellForRowAtIndexPath :.

En tableView: cellForRowAtIndexPath :, utilizamos el mismo identificador que configuramos en el Main.storyboard, CellIdentifier, que declaramos anteriormente en el tutorial. Echamos la celda a una instancia de TPSButtonCell, buscamos el elemento correspondiente de la fuente de datos y actualizamos la etiqueta de título de la celda. También agregamos un objetivo y una acción para el evento UIControlEventTouchUpInside del botón.

No olvides agregar un extracto de importación para la clase TPSButtonCell en la parte superior de TPSViewController.m.

Para evitar que la aplicación se bloquee cuando se toca un botón, implementa didTapButton: como se muestra a continuación.

Crea el proyecto y ejecútalo en el Simulador de iOS para ver lo que tenemos hasta ahora. Deberías ver una lista de películas y presionar el botón en los registros correctos un mensaje a la consola de Xcode. Estupendo. Es hora de la sustancia del tutorial.

4. ¿Cómo no hacerlo?

Cuando el usuario toque el botón de la derecha, enviará un mensaje de didTapButton: al controlador de vista. Casi siempre necesitas saber la ruta de índice de la celda de la vista de tabla en la que se encuentra el botón. Pero, ¿cómo se obtiene esa ruta de índice? Como mencioné, hay tres enfoques que puedes tomar. Primero veamos cómo no hacerlo.

Echa un vistazo a la implementación de didTapButton: y trata de descubrir qué le sucede. ¿Ves el peligro? Deja que te ayude. Ejecuta primero la aplicación en iOS 7 y luego en iOS 6. Echa un vistazo a las salidas de Xcode a la consola.

El problema con este enfoque es que es propenso a errores. En iOS 7, este enfoque funciona bien. En iOS 6, sin embargo, no funciona. Para que funcione en iOS 6, debes implementar el método como se muestra a continuación. La jerarquía de vistas de varias subclases de UIView comunes, como UITableView, ha cambiado en iOS 7 y el resultado es que el enfoque anterior no produce un resultado consistente.

¿No podemos simplemente verificar si el dispositivo está ejecutando iOS 7? Esa es una muy buena idea. Sin embargo, ¿qué harás cuando iOS 8 cambie la jerarquía de vistas internas de UITableView una vez más? ¿Vas a parchear tu aplicación cada vez que se presente una versión principal de iOS? ¿Y qué hay de todos aquellos usuarios que no actualizan a la última versión (parchada) de su aplicación? Espero que esté claro que necesitamos una mejor solución.

5. Una mejor solución

Un mejor enfoque es inferir la ruta de índice de la celda en la vista de tabla en función de la posición sender, la instancia de UIButton, en la vista de tabla. Usamos convertPoint:toView: para lograr esto. Este método convierte el centro del botón del sistema de coordenadas del botón al sistema de coordenadas de la vista de tabla. Entonces se vuelve muy fácil. Llamamos a indexPathForRowAtPoint: en la vista de tabla y le pasamos el pointInSuperview. Esto nos da una ruta de índice que podemos usar para obtener el elemento correcto de la fuente de datos.

Este enfoque puede parecer engorroso, pero en realidad no lo es. Es un enfoque que no se ve afectado por los cambios en la jerarquía de vistas de UITableView y se puede usar en muchos escenarios, incluso en vistas de colección.

6. La solución elegante

Hay una solución más para resolver el problema y requiere un poco más de trabajo. El resultado, sin embargo, es una exhibición del moderno Objective-C. Comienza volviendo a visitar el archivo de encabezado de TPSButtonCell y declara un método público denominado setDidTapButtonBlock: que acepta un bloque.

En el archivo de implementación de TPSButtonCell, crea una propiedad privada llamada didTapButtonBlock como se muestra a continuación. Ten en cuenta que la propiedad atribuida está configurada para copy, porque los bloques deben copiarse para hacer un seguimiento de su estado capturado fuera del alcance original.

En lugar de agregar un objetivo y una acción para el evento UIControlEventTouchUpInside en la tabla del controlador tableView:cellForRowAtIndexPath:, agregamos un objetivo y una acción en awakeFromNib en la clase TPSButtonCell misma.

La implementación de didTapButton: es trivial.

Esto puede parecer mucho trabajo para un botón simple, pero te mantendremos al día hasta que hayamos refactorizado tableView: cellForRowAtIndexPath: en la clase TPS ViewController. En lugar de agregar un objetivo y una acción al botón de la celda, configuramos el didTapButtonBlock de la celda. Obtener una referencia al elemento correspondiente de la fuente de datos se vuelve muy, muy fácil. Esta solución es, de lejos, la solución más elegante a este problema.

Conclusión

Aunque el concepto de bloques ha existido durante décadas, los desarrolladores de Cocoa han tenido que esperar hasta 2011. Los bloques pueden facilitar la resolución de problemas complejos y simplifican el código complejo. Desde la introducción de los bloques, Apple ha comenzado a hacer un uso extensivo de ellos en sus propias API, por lo que te animo a que sigas el ejemplo de Apple aprovechando los bloques en tus propios proyectos.

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.