iOS Dev Notes

iOS Development From The Frontlines

UITableView Tutorial

with 17 comments

UITableView is one of the most common types of views used in iOS applications. Let’s go through a tutorial of the most regularly used features of table views.

There are two main types of UITableView. The first is a “plain” table view, which looks like this:

The second is a “grouped” table view, which looks like this:

Both are commonly seen in iOS apps. Grouped table views are often seen in “settings” sections of apps, or on screens where users are inputting or editing information.

In this post we’ll be going through a sample app that uses UITableView. The code is available here.

Data

There are lots of different types of data you might be representing in your table. Maybe it’s a list of countries. Or you might be using the table more for navigation, by giving the user a list of screens they can choose to go to.

iOS keeps it pretty open-ended when it comes to how your data is represented. A UITableView is setup to support a list of rows, optionally split into sections with header names. If you don’t want to show the rows in sections, you would have one unnamed section.

This can be a bit confusing at first, as you might expect to be able to just give the table view an array of rows, and be done with it. Instead, as is common with iOS development, the delegate design pattern is used.

It may be annoying at first, but it actually provides a lot of flexibility for how you choose to represent your underlying data model. You simply define specific methods to use in order to get table information, and iOS can call them when it needs to know something like the number of rows in a section, or the content of a particular row. We’ll get into specifics below.

UITableViewDataSource vs UITableViewDelegate

One thing that makes UITableView unique is that it has two different types of delegates: UITableViewDataSource and UITableViewDelegate. This is fairly unusual in iOS. Usually an object will only have one delegate at most. This can be confusing at first (I know it was for me was I was first learning iOS).

The way to think about these two delegates is that they’re split based on functionality: for the most part, UITableViewDataSource relates to the actual data being shown in your table, whereas UITableViewDelegate deals more with the appearance and UI of the table.

To help differentiate them, here are the types of things each delegate looks for:

UITableViewDataSource:

  • Number of sections in the table
  • Number of rows in a particular section
  • Header and footer titles
  • Being told when rows are reordered, inserted and deleted
  • The particular cell at a given location in the table

UITableViewDelegate:

  • The height of a row at a given location in the table
  • Handling of selection of a row
  • Custom header and footer views for sections in the table
  • Responding to row editing

There’s some overlap between the two. For example, if you allow users to insert a new row, you may want to be notified of that action occurring (through UITableViewDelegate) and also will need to update your underlying data model (through UITableViewData Source).

There are certain delegate methods that are used over and over when working with table views, and over time you’ll probably start to forget whether they live in UITableViewDataSource or UITableViewDelegate. When you find yourself needing one of the less common methods, it can be hard to remember which delegate they belong to, so it’s a good idea to check the documentation for both if you’re not sure.

UIViewController vs UITableViewController

The class that implements the delegate methods is almost always the view controller that owns the table view. What should that class be? Most view controllers are usually subclasses of UIViewController, but iOS also provides a UITableViewController.

UITableViewController only provides a few features on top of UIViewController:

  • UITableViewController has a tableView property built-in that points to its table view.
  • UITableViewController will automatically make itself the data source and delegate, unless you specifically change it.
  • The UITableViewController will reload the table view’s data the first time it’s loaded. It will also clear any selected rows whenever the table view is displayed.
  • After the table view appears, it will flash the table view’s scroll indicators. This is a hint to the user that there’s more data than they currently see on the screen.
  • If there’s a navigation bar with an Edit/Done button, the UITableViewController will hook it up to toggle the edit mode of the table view.

Basically, it saves a little bit of time by automatically implementing some common and expected code. If you don’t want any of this behavior, you can always do it yourself within a UIViewController. Just remember to manually implement the steps listed above yourself, if you still want them. You might have seen apps where you select a row from a table, go to a new screen, and when you come back the row is still highlighted. This is usually a sign that someone used a UIViewController with their table view and forgot to clear the selection :).

The most common times when I don’t use a UITableViewController in my apps is usually when the view controller needs extra functionality beyond just a table view. Perhaps I want the table view nested inside another view, for example. Usually though, you can use a UITableViewController, as we do in the example below.

A helpful hint: if you’re creating a new view controller though Xcode (like under File -> New -> New File…), you can select “UIViewController subclass”, and then on the next screen, choose “UITableViewController” from the “Subclass of” drop down.

UITableViewDataSource

Say we have an app that displays a list of countries in a table view. (A reminder that I’ve made an example project that does this available here: UITableView-Tutorial).

We have a UITableViewController subclass called CountriesTableViewController. Countries are stored in an NSDictionary called “countries”. The keys in the dictionary are the continents in the world, and each value is an NSArray with the countries in that continent.

The first method we’ll define is “- numberOfSectionsInTableView:”. This is the number of keys in the countries dictionary:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return [self.countries count];
}

Now we add a way to get the title of a given section:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    return [[self.countries allKeys] objectAtIndex:section];
}

Next, we provide a way to return the number of rows for a given section. We can use the method we just defined to get the name of the continent:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSString *continent = [self tableView:tableView titleForHeaderInSection:section];
    return [[self.countries valueForKey:continent] count];
}

The one required method left is to return the cell for a particular row.

cellForRowAtIndexPath and UITableViewCells

The method “- tableView:cellForRowAtIndexPath:” is an important one. We’re given an index path. An index path is an object that contains both a section and a row. Given this information we can determine which row we’re looking for and configure a UITableViewCell to match it.

The class UITableViewCell represents a single cell (or row) in the table view. If a table has thousands of rows (say, in a list of contacts), then for iOS to have to create a thousand UITableViewCell objects would be a large resource hog. Also, when a user swipes down a table view list, iOS has to setup these rows very quickly, and creating separate UITableViewCell objects as you scroll takes a lot of time.

This is why UITableViewCell uses the idea of a “cell identifier”. When you create a UITableViewCell, you can give it a cell identifier string. After that, calling “dequeueReusableCellWithIdentifier” will attempt to return a pre-existing UITableViewCell, saving iOS from having to create a new one. It can cycle through 10 or so table view cells that are visible at any one time.

You usually only need different cell identifiers for each separate cell layout in your table. In many cases, there is only one, if each cell is laid out the same way. That is also the case for our example.

UITableViewCell is a very versatile class. It has a number of styles pre-defined, including a single line of text, a line of text with a subtitle, and image views. You can also setup the accessory view, which is the symbol you sometimes see on the right side of the cell (often as a “>”). If you need to further customize UITableViewCell, you can get access to its contentView, or even subclass UITableViewCell for complete control.

Let’s setup a simple cell with the country name on the left, and an accessory indicator on the right:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"CountryCell";
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
 
    // Configure the cell...
    NSString *continent = [self tableView:tableView titleForHeaderInSection:indexPath.section];
    NSString *country = [[self.countries valueForKey:continent] objectAtIndex:indexPath.row];
 
    cell.textLabel.text = country;
 
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
 
    return cell;
}

UITableViewDelegate

The code we’ve written above is actually enough to show the list of countries within sections of continents. However, it doesn’t provide a way to respond to any actions, such as the user tapping on a row of the table. That’s where UITableViewDelegate comes in.

Let’s add in some code that presents the country in a UIAlertView when its tapped. For this we define a method called “– tableView:didDeselectRowAtIndexPath:”.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *continent = [self tableView:tableView titleForHeaderInSection:indexPath.section];
    NSString *country = [[self.countries valueForKey:continent] objectAtIndex:indexPath.row];
 
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:[NSString stringWithFormat:@"You selected %@!", country] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alert show];
    [alert release];
 
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}

We’re given an indexPath, which we can use to find the country that was selected. Then we display that in an alert. Lastly, we deselect the row. Without this last part, the row would stay highlighted after being tapped.

Conclusion

I hope you’ve found this UITableView tutorial helpful. UITableView and its related classes are used in almost every iOS app. There were some more complex things that I didn’t talk about here, including inserting, reordering and deleting rows, and coding up custom UITableViewCells. If you’d like to know more about those or any other topics, let me know in the comments! It might be our next tutorial!


Looking to add push notifications to your iOS app? Check out PushLayer, a service from the authors of iOSDevNotes.

Bookmark and Share

Subscribe for updates

Fill out the form below to join my newsletter. I'll also send you a bonus copy of my ebook, "5 Steps To Becoming a Better iOS Developer".

Written by cwalcott

October 5th, 2011 at 10:53 am

17 Responses to 'UITableView Tutorial'

Subscribe to comments with RSS or TrackBack to 'UITableView Tutorial'.

  1. Thanks very much for this tutorial, it really helped me a lot, with all the outdated tutorials about this it was hard to understand what to do for a noob.

    finally i know how to use this and i combined it successfully with a navigation controller thanks a lot.

    btw i think ur violating the NDA by this tut so take it down for 3 days and put it back up because the official release of iOS 5 will be on th 14th of october

    Shereef Marzouk

    11 Oct 11 at 5:15 am

  2. Thanks, I’m glad you found it useful!

    I don’t think I’m violating the NDA, I’m not talking about anything iOS 5-specific.

    cwalcott

    11 Oct 11 at 10:50 am

  3. Nice work! No NDA violation here – this is all standard iOS stuff, nothing iOS 5 specific.

    n.b.: One other thing UITableViewController tackles is graceful resizing and scrolling of a UITableView when the keyboard comes into play (such as with a text field). That said/written, yes, I do often end up using UIViewController for other reasons. I wonder if there’s a way to stick with UITableViewController but still use a top level VC as well – best of both worlds? Hmm …

    Joe D'Andrea

    11 Oct 11 at 1:17 pm

  4. Hi,

    Just wanted to make a contribution to your tutorial, and although it might seem as a shameless plug, here it is; it’s a tutorial on how to customize UITableView’s grouped look so you don’t have to have only plain and grouped views.
    The tutorial is here – http://www.planet1107.net/custom-grouped-cell-205

    Cheers!

    Planet1107.net

    17 Oct 11 at 8:31 pm

  5. I need to make the exact country list UITableview with addition of a country flag image in each row next to the name. Whats the best way to accomplish this without effecting performance?

    Thanks

    Tom

    2 Nov 11 at 4:04 am

  6. Hi Tom,
    Take a look at http://www.iosdevnotes.com/2011/01/loading-remote-images-into-a-uitableviewcell/ which describes loading images into cells asynchronously.

    cwalcott

    2 Nov 11 at 3:26 pm

  7. Thank you so much for sharing!!

    This tutorial has helped me a lot. I’m a newbie when it comes to iphone and objective-c development.

    You commented on the tutorial the navigation bar with an Edit/Done button. I know this is not a tutorial on this but I was wondering if you could help me.

    If I have the navigation bar and I click Edit, is it possible to add more commands to the left of the item? If you could point me in the right direction I would greatly appreciate it.

    Thanks!!

    Alejandro Salas

    20 Mar 12 at 3:29 am

  8. Great tutorial, very easy to follow. What I am struggling with is having a list of items that can then be linked into core data to add the next level of information. For example Countries and Counties in the tableview, with core data adding in the towns in each country and editable by users. I can do the table, and the coredata seperate, just struggling to marry the two. Any suggestions?

    Christina

    20 Mar 12 at 11:24 pm

  9. Hello Christina, I don’t know if this will help you, it’s a project that might be related to what you are trying to do:

    https://github.com/brianstewart/ToDo

    Alejandro Salas

    26 Mar 12 at 4:03 pm

  10. Hello,

    I was wondering if you knew of a simple explanation of how to take the item selected, and pass it to the outer View Controller.

    I have everything work, and can create the alert message (as you example has). But I want to take the selected value, and then update another label on the outer View Controller. I know you have to use a delegate but I can’t seem to figure out how.

    Carl

    5 Apr 12 at 4:00 pm

  11. [...] this links for more info 1) Table view tutorial 2) StackOverflow similar question Tagged: iosiPhoneobjective-cquestionsuitableviewUIview [...]

  12. [...] would recommend that you start by reading a tutorial such as this one: http://www.iosdevnotes.com/2011/10/uitableview-tutorial/ , which looks fairly thorough to me. It explains how to set your datasource for the table and how [...]

  13. Nice Tutorial! I did another one to set up custom tableviewcells, but it works with xib files and I cannot bring it to storyboards. I am not a programmer, just a designer who wants to get his foot into the developer world.

    Halunke

    30 Jun 12 at 10:23 am

  14. Good, thorough tutorial, but I have a question. Do you have to repeat the -(UITableViewCell *) method for each cell you want in the grouped table? Or do you add each cell inside that one method?

    Duncan

    15 Jul 12 at 3:23 pm

  15. Never mind, i found my answer.

    Duncan

    15 Jul 12 at 3:37 pm

  16. Very very good tutorial. Very well explained it helped me a lot.
    This kind of tutorial are very helpful for developers so .. THANK YOU!

    Pablo

    31 Aug 12 at 12:29 am

  17. Doesn’t matter how hard I try, my tables are always loading in an unordered way. For example, Cygnus Olor section comes sometimes before Tadorna tadorna and sometimes after it.
    Any idea why the plist doesn’t show in the order I wrote it ?

    Bernard

    19 Dec 12 at 9:39 am

Leave a Reply