iOS desde Cero: Table Views (UITableView)

Publicado el 08 julio 2013 por Codehero @codeheroblog

En este capitulo de iOS desde Cero hablaremos del componente de interfaz gráfica más utilizado de iOS, los Table Views.


Preparando el proyecto en Xcode

En esto ya somos unos expertos. Abrimos Xcode y creamos un nuevo proyecto (Seleccionamos “Single View Application” y quitamos el check de Use storyboards; seguiremos usando Xib’s por ahora)

Vamos a ViewController.xib y agregamos un “Table View” desde el inspector a nuestra vista.

Ahora conectamos el table view con el código. La manera más fácil es la que aprendimos en la lección anterior. Colocamos el editor en modo asistente y haciendo Option+click arrastramos desde el controlador hasta el código

Ya tenemos el table view conectado al código, ahora vamos a agregar un array como un atributo de la clase para guardar lógicamente el contenido de nuestra tabla.

{
    NSArray *content;
}

NSArray es la clase incluida por Apple en el Foundation Framework, para manejar arrays. Luego de incluirlo nuestra clase debería lucir de la siguiente manera:

#import UIKit.h>

@interface ViewController : UIViewController
{
    NSArray *content;
}

@property (weak, nonatomic) IBOutlet UITableView *tableView;

@end

Ahora vamos a introducir un concepto del cual no habíamos hablado, los protocolos.


Fuente de datos y delegados

Objective-C las interfaces son llamadas protocolos. En nuestra clase ViewController necesitamos implementar dos de estos para poder llenar de contenido la tabla y recibir los eventos que se generen en la vista:


Esto lo colocamos justo al lado del nombre de la clase de la cual heredamos en el encabezado. Luego, nuestra clase debe lucir de la siguiente manera:

#import UIKit.h>

@interface ViewController : UIViewController 
  • UITableViewDataSource: Los data source se encargan de cargar cargar los datos que va a mostrar la tabla en sus celdas.
  • UITableViewDelegate: Los delegados son los que manejan los eventos disparados desde otra clase. En este caso la clase UITableView va a recibir ciertas acciones por parte de los usuarios y las va a delegar a nuestra clase ViewController.

Vamos ahora al método viewDidLoad en la implementación de nuestra clase (archivo .m). Aquí tenemos que decirle a nuestro table view que su fuente de datos y su delegado son esta clase.

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [self.tableView setDataSource:self];
    [self.tableView setDelegate:self];
}

Llamando a los metodos setDataSource: y setDelegate: de UITableView, asignamos como fuente de datos y delegado de la tabla a nuestra clase.


Llenando la tabla

En UITableViewDataSource están los métodos que la tabla invoca para llenarse de contenido cuando es cargada desde el archivo Xib. Nosotros debemos implementar estos métodos para que nuestro table view pueda cargar contenido en sus celdas.

Pero primero lo primero. Tenemos que llenar el array con algo para poder mostrar en la tabla. Yo lo voy a llenar de nombres de ciudades del mundo:

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [self.tableView setDataSource:self];
    [self.tableView setDelegate:self];
    
    content = @[@"Caracas",
                @"Los Angeles",
                @"Nueva York",
                @"Buenos Aires",
                @"Madrid",
                @"Berlin",
                @"Londres",
                @"Moscú",
                @"Hong Kong",
                @"Tokio",
                @"Melbourne"];
}

Esta es la forma literal de declarar un NSArray, también podrías hacerlos mediante el constructor [[NSArray alloc] initWithObjects:…] pero sería más largo.

Ya tenemos el contenido, ahora veamos como colocarla en la tabla.

En el data source hay 2 métodos requeridos que debemos implementer para que funcione el table view: tableView:cellForRowAtIndexPath: y tableView:numberOfRowsInSection:.

En tableView:numberOfRowsInSection: retornamos el número de filas que queremos en la tabla, en este caso es la cantidad de ciudades en el array:

- (NSInteger)tableView:(UITableView *)tableView
 numberOfRowsInSection:(NSInteger)section
{
    return [content count];
}

En tableView:cellForRowAtIndexPath: retornamos la celda que va en el indice dado. La diferencia es que este método tiene un formato recomendado en su estructura:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"myCell"];
    
    if (!cell)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                      reuseIdentifier:@"myCell"];
    }
}

Este método debería tener siempre esta estructura. Cuando desplazamos la tabla, vamos dejando celdas fuera de la pantalla, estas celdas las podemos reutilizar para aprovechar memoria. Con este código extraemos una celda que ya no sea visible y la reutilizamos, es por esto también que tienen un identificador de reuso (reuse identifier), para saber que tipo de celda es y saber si la puede reutilizar para el próximo indice a mostrar.

Luego de este bloque podemos agregar el contenido y estilizar la celda como queramos, por ahora yo solo voy a mostrar el nombre de la ciudad que guardo en el array. En la propiedad “Etiqueta de texto” de las celdas, voy a asignar el nombre de la ciudad que esta en el indice dado:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"myCell"];
    
    if (!cell)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                      reuseIdentifier:@"myCell"];
    }
    
    NSString *cityName = [content objectAtIndex:indexPath.row];
    [cell.textLabel setText:cityName];
    
    return cell;
}

Ahora si corremos la aplicación deberíamos ver la tabla llena con los nombres de las ciudades.

Delegando los toques de recibe la tabla

En el delegado no hay métodos requeridos para ser implementados. Dependiendo de nuestra necesidad podemos implementar el que mejor se ajuste al requerimiento. Yo voy a implementar el más común en mis proyectos, tableView:didSelectRowAtIndexPath:, este es invocado cuando el usuario selecciona una celda de la tabla:

- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"Seleccionaste el indice: %i", indexPath.row);
}

Aquí imprimí por consola el indice seleccionado.


Conclusión

En este capítulo aprendimos los conceptos básicos para implementar un table view en nuestra app. Para ampliar tus conocimientos y experimentar con otros métodos de UITableViewDataSource y UITableViewDelegate visita la documentación de Apple sobre data source y delegate.

En estas direcciones está todo lo que necesitas saber sobre estos protocolos, solo que está en inglés. Si tienes alguna duda o no sabes inglés puedes dejar tus preguntas abajo en los comentarios.

Saludos.