iOS UI Table View (UITableView)

UITableView Overview

  • A subclass of UIScrollView
  • Use to display data as rows/cells in a Table View

Application Delegate to create a Table View

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    MyTableViewController tableView = [[MyTableViewController alloc] init];

    UINavigationController *navigation = [[UINavigationController alloc] init];
    [navigation pushViewController:tableView animated:NO];

    [window addSubview:navigation.view];

    [window makeKeyAndVisible];
    return YES;
}
  • Create a table view (inherited from UITableViewController) and a navigation controller
  • Push the table view controller to the navigation controller
  • Set the navigation controller's view to the window
  • UITableViewController can be initialized with 2 different styles
    - (id)initWithFrame:(CGRect)aRect style:(UITableViewStyle)style;
    
    • UITableViewStylePlain (Similar to iPhone Contact list)
    • UITableViewStyleGrouped (Data are grouped like iPhone System setting)

Get UI Table View Data (UITableViewDataSource Delegate Protocol)

Implement MyTableViewController

  • A subclass of UITableViewController
  • UITableViewController is a convenience class to implement the Table View with the associated table data
  • MyTableViewController will implement the UITableViewDataSource protocol to supply data to the Table View
    MyTableViewController.h
    @interface MyTableViewController : UITableViewController
    

The data in a table view can be divided into sections

    • For example, contact list data can be divided into sections according to the first character of the user name
    • Section a, b, c, d ..., z

MyTableViewController overrides the following methods to provide the table's data

  • Return the number of sections in the table
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    {
        return ...
    }
    
  • Return the number of rows in the specific section
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return ...;
    }
    
  • Return a row data (as UITableViewCell) specified by indexPath: section/row
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *CellIdentifier = @"MyTableViewCell";
    
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                  reuseIdentifier:CellIdentifier];
        }
    
        cell.textLabel.text = [self getMyData:indexPath];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    
        return cell;
    }
    
  • Accessing data in a specific section of a specific row
    - (NSString *)getMyData:(NSIndexPath *)indexPath
    {
           // Access the section number by: indexPath.section
           // Access the row number by: indexPath.row
    
    	...
    }
    

Drawing each row: UITableViewCell

UITableViewCell is an UIView subclass for drawing a row/cell in the table view

Initialize a new cell's view

- initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseId;
  • Four different cell style
    UITableViewCellStyleDefault
    UITableViewCellStyleSubtitle
    UITableViewCellStyleValue1
    UITableViewCellStyleValue2
    

Setting the cell data

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
  ...
  cell.textLabel.text = [myText objectAtIndex:indexPath.row];
  cell.detailTextLabel.text = [myDetail objectAtIndex:indexPath.row];
  cell.imageView.image = [myImage objectAtIndex:indexPath.row];
  cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  ...
}

For performance reason, the following code re-use the object UITableViewCell

  • Will re-use a pool of free UITableViewCell
  • Create new one only none is free in the pool
     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        if (cell == nil) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                  reuseIdentifier:CellIdentifier];
        }
    

Changing Look and Feel for Each Cell and Section

UITableView composes of

  • Table Header
  • Zero or more Sections
    • Each section composes of Section Header, Table Cells, Section Footer
  • Table Footer

Change the accessor in each cell

    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  • The look and feel in accessing or selecting a specific row
  • To present an arrow in the cell to allow user to look into the details of a cell, use
    • UITableViewCellAccessoryDisclosureIndicator (A simple arrow button) or
    • UITableViewCellAccessoryDetailDisclosureButton (A round blue arrow button)
  • To present a check mark in a cell
    • UITableViewCellAccessoryCheckmark

To change and control the cell accessory for each row programmatically, implement the following delegate

- (UITableViewAccessoryType)tableView:(UITableView *)sender
     accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath
{
  ...
}

To change the header/footer title when move to a different session, implement the following delegate

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
...
}

To change the header/footer view, implement the following delegate

- (UIView *)tableView:(UITableView *)sender
    viewForHeaderInSection:(NSInteger)section;

Returning the title of all sections

- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
   ...
}

To change the height of the row

- (CGFloat)tableView:(UITableView *)sender
  heightForRowAtIndexPath:(NSIndexPath *)indexPath;

To override the built-in arrow icon as the accessor, you can provide a custom UI view for accessor through the accessoryView property

@property (retain) UIView *accessoryView;

To use a custom UI View for each cell, set the contentView property

@property (readonly) UIView *contentView;

Cell Editing

To support cell editing, implement:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
                                           forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // For delete request
	NSString *section = [self.sections objectAtIndex:indexPath.section];

        // Delete the row from the data
        ...

	// Delete the row from the table view
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                   withRowAnimation:UITableViewRowAnimationFade];
    }  else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Add the new row to the data and the table view
        ...
    }
}

Every cell can be deleted until

- (BOOL)tableView:(UITableView *)sender canEditRowAtIndexPath:(NSIndexPath *)indexPath;
  • Return NO for indexPath

Row Re-ordering

To allow a row to move, return YES for indexPath

- (BOOL)tableView:(UITableView *)sender canMoveRowAtIndexPath:(NSIndexPath *)indexPath;

Show the reorder control

cell.propertyBOOLshowsReorderControl = YES

To support row re-ordering, implement

- (void)tableView:(UITableView *)sender
    moveRowAtIndexPath:(NSIndexPath *)sourcePath
           toIndexPath:(NSIndexPath *)destinationPath;

Delegate for UITableView

Handler when a row is selected

  • Navigate to another view after select the row (Possibly showing the detail of a row)
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
       MyDetailViewController *detail = [[MyDetailViewController alloc] init];
    
       // Set the data for the Detail View controller
       ...
    
       [self.navigationController pushViewController:detail animated:YES];
    }
    

Before a cell is displayed

- (void)tableView:(UITableView *)sender
     willDisplayCell:(UITableViewCell *)cell
     forRowAtIndexPath:(NSIndexPath *)indexPath;