Codepath

Table View Quickstart

Overview

Table views are used to display grouped lists of cells. Here are some example of table views:

This guide provides a quick recipe for setting up and using a simple table view with Interface Builder and prototype cells.

Table view setup

The example we'll use throughout is a table with cities and state abbreviations.

Step 1: Determine what data you need to display in the table

Figure out what data you need to display in the table and how you're going to obtain this data. In our example, we have a static fixture which is a list of strings.

let data = ["New York, NY", "Los Angeles, CA", "Chicago, IL", "Houston, TX",
        "Philadelphia, PA", "Phoenix, AZ", "San Diego, CA", "San Antonio, TX",
        "Dallas, TX", "Detroit, MI", "San Jose, CA", "Indianapolis, IN",
        "Jacksonville, FL", "San Francisco, CA", "Columbus, OH", "Austin, TX",
        "Memphis, TN", "Baltimore, MD", "Charlotte, ND", "Fort Worth, TX"]
NSArray *data;
- (void)viewDidLoad {
    [super viewDidLoad];
    data = @[@"New York, NY", @"Los Angeles, CA", @"Chicago, IL", @"Houston, TX",
             @"Philadelphia, PA", @"Phoenix, AZ", @"San Diego, CA", @"San Antonio, TX",
             @"Dallas, TX", @"Detroit, MI", @"San Jose, CA", @"Indianapolis, IN",
             @"Jacksonville, FL", @"San Francisco, CA", @"Columbus, OH", @"Austin, TX",
             @"Memphis, TN", @"Baltimore, MD", @"Charlotte, ND", @"Fort Worth, TX"];
}

Step 2: Add table view to view controller

In your storyboard drag a Table View (not Table View Controller) from the Object Library into your view controller. Add an outlet for the table view in your view controller.

Step 3: Add prototype cell to table view

You'll need a way to describe the appearance of the cells in your table. One way to design a reusable template for cells is to use prototype cells. Drag a Table View Cell from the Object Library into your table view. You can now design the layout of the prototype cell by adding more components from the Object Library.

Step 4: Create custom class for prototype cell

Select File -> New -> File... -> iOS -> Source -> Cocoa Touch Class and create a new subclass of UITableViewCell. Make sure to select Swift as the language as well:

Back in Interface Builder select your prototype cell, and in the Identity Inspector, set its custom class property to the new class you just created.

Step 5: Add code that allows configuration of your prototype cell

Open the Assistant Editor (two overlapping rings view) and select your custom class. Add outlets for elements you need to configure. You might need to add code to provide an easy way to configure the cell.

The example cell is simple enough that we don't have to do anything special. It just has two labels one for the city and one for the state abbreviation.

class DemoPrototypeCell: UITableViewCell {
    @IBOutlet weak var cityLabel: UILabel!
    @IBOutlet weak var stateLabel: UILabel!
}
//  DemoPrototypeCell.h

#import <UIKit/UIKit.h>

@interface DemoPrototypeCell : UITableViewCell

@property (weak, nonatomic) IBOutlet UILabel *cityLabel;
@property (weak, nonatomic) IBOutlet UILabel *stateLabel;

@end

Step 6: Set prototype cell's reuse identifier

You'll need a way to tell the table view to create and reuse instances of this prototype cell. Select the cell and give it a unique identifier in the Attributes Inspector.

Step 7: Set the table view's data source and delegate

Declare your view controller to implement the UITableViewDataSource and UITableViewDelegate protocols. In your view controller's viewDidLoad method, set the table view's data source and delegate properties to your view controller.

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
        tableView.delegate = self
    }
}
//  ViewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@end

//  ViewController.m

#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.tableView.dataSource = self;
    self.tableView.delegate = self;
}
@end

Step 8: Implement data source methods

Implement methods from the UITableViewDataSource protocol in order to tell the table what data to show in its table.

Return the total number of rows from the numberOfRowsInSection method.

The cellForRowAtIndexPath requires that a table view cell is obtained, configured with the correct data and returned. Get an instance of the prototype cell by calling dequeueReusableCellWithIdentifier on the table view and passing it the identifier that was set in Step 6.

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    @IBOutlet weak var tableView: UITableView!

    let data = ["New York, NY", ...,  "Fort Worth, TX"]

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
        tableView.delegate = self
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "com.codepath.DemoPrototypeCell", for: indexPath) as! DemoPrototypeCell
        let cityState = data[indexPath.row].components(separatedBy: ", ")
        cell.cityLabel.text = cityState.first 
        cell.stateLabel.text = cityState.last 
        return cell
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return data.count
    }
}
//  ViewController.m

#import "ViewController.h"
#import "DemoPrototypeCell.h"

@implementation ViewController

NSArray *data;

- (void)viewDidLoad {
    [super viewDidLoad];
    data = @[@"New York, NY", @"Los Angeles, CA", @"Chicago, IL", @"Houston, TX",
             @"Philadelphia, PA", @"Phoenix, AZ", @"San Diego, CA", @"San Antonio, TX",
             @"Dallas, TX", @"Detroit, MI", @"San Jose, CA", @"Indianapolis, IN",
             @"Jacksonville, FL", @"San Francisco, CA", @"Columbus, OH", @"Austin, TX",
             @"Memphis, TN", @"Baltimore, MD", @"Charlotte, ND", @"Fort Worth, TX"];
    self.tableView.dataSource = self;
    self.tableView.delegate = self;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    DemoPrototypeCell *cell = [tableView dequeueReusableCellWithIdentifier:@"com.codepath.DemoPrototypeCell" forIndexPath:indexPath];
    NSArray *cityState = [data[indexPath.row] componentsSeparatedByString:@", "];
    cell.cityLabel.text = cityState.firstObject;
    cell.stateLabel.text = cityState.lastObject;
    return cell;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return data.count;
}

Table view usage

This section covers some common issues that arise when using table view cells.

Automatically resize row heights

Sometimes you'll have cells that vary in height. The easiest way to have your rows be correctly sized is to set an estimatedRowHeight and set the table view's rowHeight to UITableViewAutomaticDimension. This only works in iOS 8 and above. For a discussion of other ways to have variable row heights see our more in depth table view guide.

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
        tableView.estimatedRowHeight = 100
        tableView.rowHeight = UITableViewAutomaticDimension
    }
    ...
}
//  ViewController.m

#import "ViewController.h"

@implementation ViewController
  
  ...

- (void)viewDidLoad {
    [super viewDidLoad];
    self.tableView.dataSource = self;
    self.tableView.estimatedRowHeight = 100;
    self.tableView.rowHeight = UITableViewAutomaticDimension;
}
   
 ...

@end

Respond to row selection

The most common event you'll need respond to is the user selecting a row in the table.

Segue to another view controller

A common behavior when a row is selected, is to push a new view controller with details about that row. You can do this in a storyboard by control-dragging from the cell and selecting the appropriate segue under the Selection Segue section.

Handling row selection programmatically

You can also respond programmatically by implementing the UITableViewDelegate method didSelectRowAtIndexPath:.

  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
      tableView.deselectRow(at: indexPath, animated: true)
      // do something here
  }
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:true];
    // do something here
}

Further reading

Fork me on GitHub