Table view

In this tutorial we cover the following topics


1. General information


Technically, a table view is the view object that displays a table’s data which is an instance of the class UITableView. Each visible row in a table is implemented by an instance of the UITableViewCell class. Table views are not responsible for storing table’s data. They store only enough data to draw the rows that are currently visible. Table views get their configuration data from an object that conforms to the UITableViewDelegate protocol and their row data from an object that conforms to the UITableViewDataSource protocol.

We can also put more data, than standatd title and details one-line strings, in a cell if we need to by adding subviews to UITableViewCell. We do this using either by adding subviews programmatically when creating the cell or by loading them from a storyboard or nib file.

Table views come in two basic styles

  • Grouped A grouped table view contains one or more sections of rows. Within each section, all rows sit tightly together in a nice little group; but between sections, there are clearly visible gaps.
  • Plain This is the default style. In this style, the sections are slightly closer together, and each section’s header can optionally be styled in a custom manner. When an index is used, this style is also referred to as indexed.

2. Introduction

  1. Step 1: create a new project
    Create a new project

    1. Select File | New | Project…
    2. Select Single View Application on the template selection sheet.
    3. As the product name enter iOS Table View Basic
    4. Set the Language to Objective-C, devices pop-up button to Universal and make sure the check box labeled Use Core Data is unchecked.
  2. Step 2: add Table view and set connections
    1. Select Main.storyboard to edit the storyboard.
    2. Find in the object library a Table View and drag it over to the View window.
      001
    3. Add all necessary constraints to make sure that the table view is positioned and sized correctly.
      002
    4. Select the table view in the Document Inspector and bring up the Connections Inspector (use shortcut: Alt + Command + 6).
    5. Drag from the circle next to dataSource and delegate in Outlets section to the View Controller icon in the Document Outline or above the view controller in the storyboard editor. This makes our controller class both the data source and delegate for this table.
      003

      004

  3. Step 3: add a code
    1. Single-click ViewController.m and add the following code
      #import "ViewController.h"
      
      @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
      @property NSArray *eightThousandersPeaks;
      @end
      
      @implementation ViewController
      
      - (void)viewDidLoad {
          [super viewDidLoad];
          // Do any additional setup after loading the view, typically from a nib.
          self.eightThousandersPeaks =
              @[@"Mount Everest", @"K2", @"Kangchenjunga",
                @"Lhotse",@"Makalu", @"Cho Oyu",
                @"Dhaulagiri",@"Manaslu", @"Nanga Parbat",
                @"Annapurna I", @"Gasherbrum I",@"Broad Peak",
                @"Gasherbrum II", @"Shishapangma"];
      }
      
      - (void)didReceiveMemoryWarning {
          [super didReceiveMemoryWarning];
          // Dispose of any resources that can be recreated.
      }
      
      - (NSInteger)tableView:(UITableView *)tableView
       numberOfRowsInSection:(NSInteger)section {
          return [self.eightThousandersPeaks count];
      }
      
      - (UITableViewCell *)tableView:(UITableView *)tableView
               cellForRowAtIndexPath:(NSIndexPath *)indexPath {
          static NSString *TableIdentifier = @"TableIdentifier";
          UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
                                   TableIdentifier];
          if (cell == nil) {
              cell = [[UITableViewCell alloc]
                      initWithStyle:UITableViewCellStyleDefault
                      reuseIdentifier:TableIdentifier];
          }
          cell.textLabel.text = self.eightThousandersPeaks[indexPath.row];
          return cell;
      }
      
      @end
      

      005

  4. Step 4: using table view cell styles
    Except the default UITableViewCellStyleDefault the UITableViewCell class includes several other predefined cell styles that let us easily add a bit more variety to our table views. These cell styles use three different cell elements

    • Text label This is the cell’s main text. In the case of the default style UITableViewCellStyleDefault the text label is the only text shown in the cell.
    • Detail text label This is the cell’s secondary text, usually used as an explanatory note or label.
    • Image If an image is part of the specified style, the image is displayed to the left of the cell’s text.

    Let’s see all of them

    1. Single-click ViewController.m and to enable detail text label add/modify the following code
      			
      @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
      ...
      @property NSArray *eightThousandersPeaksHeight;
      @end
      
      - (void)viewDidLoad {
      ...
          //Height above mean sea level (AMSL)
          self.eightThousandersPeaksHeight =
          @[@"8850", @"8611", @"8586",
            @"8516",@"8463", @"8201",
            @"8167",@"8156", @"8126",
            @"8091", @"8068",@"8047",
            @"8035", @"8013"];
      }
      
      - (UITableViewCell *)tableView:(UITableView *)tableView
               cellForRowAtIndexPath:(NSIndexPath *)indexPath {
       ...
           if (cell == nil) {
              cell = [[UITableViewCell alloc]
                      initWithStyle:UITableViewCellStyleSubtitle
                      reuseIdentifier:TableIdentifier];
          }
      ...
          cell.detailTextLabel.text = self.eightThousandersPeaksHeight[indexPath.row];
          
          return cell;
      }
      

      006

    2. Let’s change table view cell styles again. Single-click ViewController.m and to enable detail text label add/modify the following code
      ...			
         if (cell == nil) {
              cell = [[UITableViewCell alloc]
                      initWithStyle:UITableViewCellStyleValue1
                      reuseIdentifier:TableIdentifier];
          }
      ....
      

      007

    3. Let’s change table view cell styles again. Single-click ViewController.m and to enable detail text label add/modify the following code
      ...			
         if (cell == nil) {
              cell = [[UITableViewCell alloc]
                      initWithStyle:UITableViewCellStyleValue2
                      reuseIdentifier:TableIdentifier];
          }
      ....
      

      008

  5. Step 5: add an image
    For all style except UITableViewCellStyleValue2 we can add an icon.

    1. Prepare two images: one for selected row and second for not selected. In my case I have two dots (red and green) 24 pixels height and 24 pixels width.
    2. Drag both files to your project’s Images.xcassets.
    3. Single-click ViewController.m and to enable detail text label add/modify the following code
      	
      - (UITableViewCell *)tableView:(UITableView *)tableView
               cellForRowAtIndexPath:(NSIndexPath *)indexPath {
          
          ...
          
          UIImage *image = [UIImage imageNamed:@"dot_green"];
          cell.imageView.image = image;
          UIImage *highlightedImage = [UIImage imageNamed:@"dot_red"];
          cell.imageView.highlightedImage = highlightedImage;
          
          ...
          
      }		
      

  6. Step 6: handling row selection
    Now we are going to implement the delegate method that is called after a row has been selected.

    1. Single-click ViewController.m and add/modify the following code
      	
      - (void)tableView:(UITableView *)tableView
      didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
          NSString *textLabel = self.eightThousandersPeaks[indexPath.row];
          
          UITableViewCell *selectedCell = [tableView cellForRowAtIndexPath:indexPath];
          NSString *detailTextLabel = selectedCell.detailTextLabel.text;
          
          NSString *message = [[NSString alloc] initWithFormat:
                               @"You selected %@ (%@)", textLabel, detailTextLabel];
          
          UIAlertController *controller =
          [UIAlertController alertControllerWithTitle:@"Information"
                                              message:message
                                       preferredStyle: UIAlertControllerStyleAlert];
          UIAlertAction *cancelAction =
          [UIAlertAction actionWithTitle:@"Close"
                                   style: UIAlertActionStyleDefault
                                 handler: nil];
          [controller addAction:cancelAction];
          [self presentViewController:controller animated:YES completion:nil];
          [tableView deselectRowAtIndexPath:indexPath animated:YES];
      }	
      

      010

  7. Step 7: disable row selection
    The second table’s delegate method that allow us to handle row selection is tableView:willSelectRowAtIndexPath which is called before the row is selected. It can be used to prevent the row from being selected or even to change which row gets selected.

    1. Single-click ViewController.m and add/modify the following code
      	
      - (NSIndexPath *)tableView:(UITableView *)tableView
        willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
          
          NSString *detailTextLabel = self.eightThousandersPeaksHeight[indexPath.row];
          
          NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
          f.numberStyle = NSNumberFormatterDecimalStyle;
          NSNumber *height = [f numberFromString:detailTextLabel];
          
          if (height.integerValue < 8500) {
              return nil;
          } else {
              return indexPath;
          }
      }
      
  8. Step 8: delete TableView row when swipe
    1. Single-click ViewController.m and add/modify the following code
      @interface ViewController () <UITableViewDataSource, UITableViewDelegate>
      @property NSArray *eightThousandersPeaksInit;
      @property NSArray *eightThousandersPeaksHeightInit;
      @property NSMutableArray *eightThousandersPeaks;
      @property NSMutableArray *eightThousandersPeaksHeight;
      @end
      	
      - (void)viewDidLoad {
          [super viewDidLoad];
          // Do any additional setup after loading the view, typically from a nib.
          self.eightThousandersPeaksInit =
              @[@"Mount Everest", @"K2", @"Kangchenjunga",
                @"Lhotse",@"Makalu", @"Cho Oyu",
                @"Dhaulagiri",@"Manaslu", @"Nanga Parbat",
                @"Annapurna I", @"Gasherbrum I",@"Broad Peak",
                @"Gasherbrum II", @"Shishapangma"];
          self.eightThousandersPeaks = [NSMutableArray arrayWithArray:self.eightThousandersPeaksInit];
          // or
          //self.eightThousandersPeaks = [self.eightThousandersPeaksInit mutableCopy];
          //Height above mean sea level (AMSL)
          self.eightThousandersPeaksHeightInit =
          @[@"8850", @"8611", @"8586",
            @"8516",@"8463", @"8201",
            @"8167",@"8156", @"8126",
            @"8091", @"8068",@"8047",
            @"8035", @"8013"];
          //self.eightThousandersPeaksHeight = [NSMutableArray arrayWithArray:self.eightThousandersPeaksHeightInit];
          // or
          self.eightThousandersPeaksHeight = [self.eightThousandersPeaksHeightInit mutableCopy];
      }
      
      // Override to support conditional editing of the table view.
      // This only needs to be implemented if we are going to be returning NO
      // for some items. By default, all items are editable.
      - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
          // Return YES if you want the specified item to be editable.
          return YES;
      }
      
      // Override to support editing the table view.
      - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
          if (editingStyle == UITableViewCellEditingStyleDelete) {
              // Add code here for when you hit delete
              // Remove the deleted object from your data source.
              [self.eightThousandersPeaks removeObjectAtIndex:indexPath.row];
              [self.eightThousandersPeaksHeight removeObjectAtIndex:indexPath.row];
              [tableView reloadData]; // tell table to refresh now
              // or
              //[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
          }
      }
      

      011

      012

–>


3. Customizing Table View Cells with nib files


There are three basic approaches to create our own table view cells

  • one that involves loading a cell from a nib file,
  • a second that is similar, but loads the cell from a storyboard,
  • and a third that involves adding subviews to UITableViewCell programmatically when creating the cell.
  1. Step 1: create a new project
    Create a new project

    1. Select File | New | Project…
    2. Select Single View Application on the template selection sheet.
    3. As the product name enter iOS Table View Custom Cell Nib
    4. Set the Language to Objective-C, devices pop-up button to Universal and make sure the check box labeled Use Core Data is unchecked.
  2. Step 2: create a new class
    1. In the Project Navigator, right-click the iOS Table View Custom Cell Nib group and select New File….
    2. Choose Cocoa Touch Class from the iOS Source section in the template dialog and then press Next
    3. Name the new class PeakInfoCell, make it a subclass of UITableViewCell.
    4. Make sure that the Also create XIB file check box is checked this time.
    5. Press Next and then press Create to save the files.
  3. Step 3: add a new code and a table view
    1. Single-click PeakInfoCell.h and add/modify the following code
      #import <UIKit/UIKit.h>
      
      @interface PeakInfoCell : UITableViewCell
      
      @property (copy, nonatomic) NSString *name;
      @property (copy, nonatomic) NSString *height;
      
      @end
      
    2. Single-click PeakInfoCell.m and add/modify the following code
      #import "PeakInfoCell.h"
      
      @interface PeakInfoCell ()
      @property (strong, nonatomic) IBOutlet UILabel *nameLabel;
      @property (strong, nonatomic) IBOutlet UILabel *heightLabel;
      @end
      
      @implementation PeakInfoCell
      
      - (void)awakeFromNib {
          [super awakeFromNib];
          // Initialization code
      }
      
      - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
          [super setSelected:selected animated:animated];
      
          // Configure the view for the selected state
      }
      
      - (void)setName:(NSString *)name {
          if (![name isEqualToString:_name]) {
              _name = [name copy];
              self.nameLabel.text = _name;
          }
      }
      - (void)setHeight:(NSString *)height {
          if (![height isEqualToString:_height]) {
              _height = [height copy];
              self.heightLabel.text = _height;
          }
      }
      
      @end
      
    3. Single-click ViewController.h and add/modify the following code
      #import <UIKit/UIKit.h>
      
      @interface ViewController : UIViewController <UITableViewDataSource>
      
      @end
      
    4. Single-click ViewController.m and add/modify the following code
      #import "ViewController.h"
      #import "PeakInfoCell.h"
      
      static NSString *PeakInfoCellIdentifier = @"PeakInfoCellIdentifier";
      
      @interface ViewController ()
      @property NSArray *eightThousandersPeaks;
      @property NSArray *eightThousandersPeaksHeight;
      @property IBOutlet UITableView *tableView;
      @end
      
      @implementation ViewController
      
      - (void)viewDidLoad {
          [super viewDidLoad];
          // Do any additional setup after loading the view, typically from a nib.
          self.eightThousandersPeaks =
          @[@"Mount Everest", @"K2", @"Kangchenjunga",
            @"Lhotse",@"Makalu", @"Cho Oyu",
            @"Dhaulagiri",@"Manaslu", @"Nanga Parbat",
            @"Annapurna I", @"Gasherbrum I",@"Broad Peak",
            @"Gasherbrum II", @"Shishapangma"];
          //Height above mean sea level (AMSL)
          self.eightThousandersPeaksHeight =
          @[@"8850", @"8611", @"8586",
            @"8516",@"8463", @"8201",
            @"8167",@"8156", @"8126",
            @"8091", @"8068",@"8047",
            @"8035", @"8013"];
          
          UINib *nib = [UINib nibWithNibName:@"PeakInfoCell" bundle:nil];
          [self.tableView registerNib:nib
               forCellReuseIdentifier:PeakInfoCellIdentifier];
      }
      
      - (NSInteger)tableView:(UITableView *)tableView
       numberOfRowsInSection:(NSInteger)section {
          return [self.eightThousandersPeaks count];
      }
      
      - (UITableViewCell *)tableView:(UITableView *)tableView
               cellForRowAtIndexPath:(NSIndexPath *)indexPath {
      
          PeakInfoCell *cell = [tableView dequeueReusableCellWithIdentifier:
                                   PeakInfoCellIdentifier
                                   forIndexPath:indexPath];
          
          cell.name = self.eightThousandersPeaks[indexPath.row];
          cell.height = self.eightThousandersPeaksHeight[indexPath.row];
          
          return cell;
      }
      
      - (void)didReceiveMemoryWarning {
          [super didReceiveMemoryWarning];
          // Dispose of any resources that can be recreated.
      }
      
      @end
      
    5. Select Main.storyboard to edit the storyboard.
    6. Find in the object library a Table View and drag it over to the View window.
    7. Add all necessary constraints to make sure that the table view is positioned and sized correctly.
    8. Now we have to link the table view to the outlet. Select the Main.storyboard file and in the Document Outline, Control-drag from the View Controller icon to the Table View icon. Release the mouse and select tableView in the pop-up.
      020

      021

      022

    9. Set our controller class the data source for the table as we did it in part 2 step 2
      1. Select the table view in the Document Inspector and bring up the Connections Inspector (use shortcut: Alt + Command + 6).
      2. Drag from the circle next to dataSource in Outlets section to the View Controller icon in the Document Outline or above the view controller in the storyboard editor. This makes our controller class the data source for this table.
        019
  4. Step 4: design our table view cell in Interface Builder
    Create a new project

    1. select PeakInfoCell.xib in the Project Navigator to open the file for editing.
    2. Look in the library for a Table View Cell and drag it to the GUI layout area. In my case this cell was already created, so there was no need for manual addition.
      013
    3. In the the Attributes Inspector set the Identifier value to PeakInfoCellIdentifier.
    4. Select the table cell in the editing area to edit our table cell’s content view. Go to the library, drag out two Label controls, and place them in the content view where you want. Next set some font attributes. In my case left label (name label) was set to System Bold 20 and right label (height) to System Italic 17. Remember also to set correct constraints.
      014

      015

    5. Select the table view cell by clicking PeakInfoCellIdentifier in the Document Outline,
      016
      bring up the Identity Inspector, and choose PeakInfoCell as the Class in Custom Class section.
      017
    6. Switch to the Connections Inspector, where we will see the nameLabel and heightLabel outlets. Drag from the nameLabel outlet to the name label (left in my case) and from the heightLabel outlet to the height label (right).
      018
  5. Step 5: run the application
    023

4. Customizing Table View Cells with storyboard


We can also design table cells directly in the storyboard, which means that we don’t need to create an extra nib file. This is fine as long as we don’t want to share cell designs between different tables.

In this part we will do the same as in 3. Customizing Table View Cells with nib files part but without nib files.

  1. Step 1: create a new project
    Create a new project

    1. Select File | New | Project…
    2. Select Single View Application on the template selection sheet.
    3. As the product name enter iOS Table View Custom Cell Storyboard
    4. Set the Language to Objective-C, devices pop-up button to Universal and make sure the check box labeled Use Core Data is unchecked.
  2. Step 2: create a new class
    1. In the Project Navigator, right-click the iOS Table View Custom Cell Storyboard group and select New File….
    2. Choose Cocoa Touch Class from the iOS Source section in the template dialog and then press Next
    3. Name the new class PeakInfoCell, make it a subclass of UITableViewCell.
    4. Press Next and then press Create to save the files.
  3. Step 3: add a new code
    1. Single-click PeakInfoCell.h and add/modify the following code
      #import <UIKit/UIKit.h>
      
      @interface PeakInfoCell : UITableViewCell
      
      @property (copy, nonatomic) NSString *name;
      @property (copy, nonatomic) NSString *height;
      
      @end
      
    2. Single-click PeakInfoCell.m and add/modify the following code
      #import "PeakInfoCell.h"
      
      @interface PeakInfoCell ()
      @property (strong, nonatomic) IBOutlet UILabel *nameLabel;
      @property (strong, nonatomic) IBOutlet UILabel *heightLabel;
      @end
      
      @implementation PeakInfoCell
      
      - (void)awakeFromNib {
          [super awakeFromNib];
          // Initialization code
      }
      
      - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
          [super setSelected:selected animated:animated];
      
          // Configure the view for the selected state
      }
      
      - (void)setName:(NSString *)name {
          if (![name isEqualToString:_name]) {
              _name = [name copy];
              self.nameLabel.text = _name;
          }
      }
      - (void)setHeight:(NSString *)height {
          if (![height isEqualToString:_height]) {
              _height = [height copy];
              self.heightLabel.text = _height;
          }
      }
      
      @end
      
    3. Single-click ViewController.h and add/modify the following code
      #import <UIKit/UIKit.h>
      
      @interface ViewController : UIViewController <UITableViewDataSource>
      
      @end
      
    4. Single-click ViewController.m and add/modify the following code
      #import "ViewController.h"
      #import "PeakInfoCell.h"
      
      static NSString *PeakInfoCellIdentifier = @"PeakInfoCellIdentifier";
      
      @interface ViewController ()
      @property NSArray *eightThousandersPeaks;
      @property NSArray *eightThousandersPeaksHeight;
      @property IBOutlet UITableView *tableView;
      @end
      
      @implementation ViewController
      
      - (void)viewDidLoad {
          [super viewDidLoad];
          // Do any additional setup after loading the view, typically from a nib.
          self.eightThousandersPeaks =
          @[@"Mount Everest", @"K2", @"Kangchenjunga",
            @"Lhotse",@"Makalu", @"Cho Oyu",
            @"Dhaulagiri",@"Manaslu", @"Nanga Parbat",
            @"Annapurna I", @"Gasherbrum I",@"Broad Peak",
            @"Gasherbrum II", @"Shishapangma"];
          //Height above mean sea level (AMSL)
          self.eightThousandersPeaksHeight =
          @[@"8850", @"8611", @"8586",
            @"8516",@"8463", @"8201",
            @"8167",@"8156", @"8126",
            @"8091", @"8068",@"8047",
            @"8035", @"8013"];
      }
      
      - (NSInteger)tableView:(UITableView *)tableView
       numberOfRowsInSection:(NSInteger)section {
          return [self.eightThousandersPeaks count];
      }
      
      - (UITableViewCell *)tableView:(UITableView *)tableView
               cellForRowAtIndexPath:(NSIndexPath *)indexPath {
          PeakInfoCell *cell = [tableView dequeueReusableCellWithIdentifier:
                                   PeakInfoCellIdentifier];
          if (cell == nil) {
              cell = [[PeakInfoCell alloc]
                      initWithStyle:UITableViewCellStyleDefault
                      reuseIdentifier:PeakInfoCellIdentifier];
          }
          
          cell.name = self.eightThousandersPeaks[indexPath.row];
          cell.height = self.eightThousandersPeaksHeight[indexPath.row];
          
          return cell;
      }
      
      - (void)didReceiveMemoryWarning {
          [super didReceiveMemoryWarning];
          // Dispose of any resources that can be recreated.
      }
      
      @end
      
  4. Step 4: add a table view and design our table view cell in the storyboard
    1. Select Main.storyboard to edit the storyboard.
    2. Find in the object library a Table View and drag it over to the View window.
    3. Add all necessary constraints to make sure that the table view is positioned and sized correctly.
    4. Find in the object library a Table View Cell and drag it over to the Table View window.
      024
    5. In the Table View Cell section of Attributes inspector set
      1. Style as Custom,
      2. Identifier as PeakInfoCellIdentifier.
    6. Select the table cell in the editing area to edit our table cell’s content view. Go to the library, drag out two Label controls, and place them in the content view where you want. Next set some font attributes. In my case left label (name label) was set to System Bold 20 and right label (height) to System Italic 17. Remember also to set correct constraints.
      025
    7. Select Main.storyboard, then the table view cell by clicking PeakInfoCellIdentifier in the Document Outline, bring up the Identity Inspector, and choose PeakInfoCell as the Class in Custom Class section.
      026
    8. Switch to the Connections Inspector, where we will see the nameLabel and heightLabel outlets. Drag from the nameLabel outlet to the name label (left in my case) and from the heightLabel outlet to the height label (right).
    9. Now we have to link the table view to the outlet. Select the Main.storyboard file and in the Document Outline, Control-drag from the View Controller icon to the Table View icon. Release the mouse and select tableView in the pop-up.
    10. Set our controller class the data source for the table
      1. Select the table view in the Document Inspector and bring up the Connections Inspector (use shortcut: Alt + Command + 6).
      2. Drag from the circle next to dataSource in Outlets section to the View Controller icon in the Document Outline or above the view controller in the storyboard editor. This makes our controller class the data source for this table.
  5. Step 5: run the application
    027