Channels ▼

Embedded Systems

Displaying Tabular Data on iPhones

Final Fixes for Data Display

There are reasons for the lack of data on-screen. First, we haven't done anything about the other required data source method, cellForRowAtIndexPath. Search for that method in Table.m and you'll find it soon enough. It is mostly stub code, which is why the app builds and executes without errors. This method returns a reference to UITableViewCell, which is the class responsible for creating and displaying a cell. (Listing Two.)

Listing Two

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
/* add these lines of code    
	cell.textLabel.text = [products.productNames objectAtIndex:indexPath.row];
	cell.accessoryType = UITableViewCellAccessoryNone;
    return cell;

For performance reasons, the code first attempts to load a cached instance of a cell and reuse it. Otherwise, it generates one. However, this method lacks the means to retrieve and display data content. We'll add our own code to do that.

In cellForRowAtIndexPath, add these two lines:

cell.textLabel.text = [products.productNames objectAtIndex:indexPath.row];
cell.accessoryType = UITableViewCellAccessoryNone;

The first line uses the row index (provided by the supplied indexPath parameter) to access the appropriate string in the productNames array. The string is inserted into the cell's textLabel field, which then presents it in the content view of the cell. The second line of code suppresses the display of any image in the cell's accessoryView. This location is where you can add extra code to access and display content such as optional image, text, or controls.

However, when you build and run the project again, a table still doesn't appear. The second reason is because we have not added and connected the table view controller to the app's main window. Most of these changes are made to the AppDelegate class, which, following our example should be named TestTableAppDelegate. First go to the header file and add a class declaration for the Table class, then a declaration of an instance of it to the interface. Add a property declaration for an instance of Table, say, table, as well. It's important to specify this property as an IBOutlet. This makes a reference to the class appear in Interface Builder (IB). See the header TestTableAppDelegate.h for the details.

Go to the TestTableAppDelegate.m file and add a synthesize declaration for table. Follow this with a statement that adds the table view to the window:

[window addSubview:table.view];

The app delegate class generates the instance of the window automatically when it loads, so only the one line of code is required. And now, we add a final step: have IB tie the table into the app's UI.

In Xcode, double-click on the MainWindow.xib file to launch IB. In the MainWindow.xib window, choose Tools > Library to display the library of Cocoa Touch objects. Drag the Table View Controller icon from the library window into MainWindow.xib. Be sure to position the Table View Controller above the Window object, as this can affect the display hierarchy. If you expand the Table View Controller, you'll find the Table View.

Select the table view controller, and choose Tools > Identity Inspector. The Identity Inspector appears. For the Class option, we want to specify our custom table view controller, so click on the menu and choose Table from the list. The name changes in the MainWindow.xib from Table View Controller to Table. Click on TestTable App Delegate object in this window and pick Tools > Connections Inspector. The Connection Inspector appears. You'll see your instance of table as an outlet. Click and drag from the table connection to the Table object in MainWindow.xib. This action has just created the link between table and the app's UI. Save your changes in IB, then build and run the app. Now a list of Apple products appears. As you see, there are a lot of little details to get right when presenting a table. However, now that you understand them, building subsequent tables should be much easier.

The other thing to notice is that the code uses a delegate protocol, willDisplayCell. This method allows you to modify a cell's appearance prior to its being displayed, and the code in the example app alternates between two background colors to help the rows stand out.

Sectioning the Data

Now, let's go to the next level and divide the content into sections. To do this, we'll slice the product list into categories and make those into table sections. Modifying the existing Table object, as shown in Listing Three, easily does this. Note how the list has been broken into arrays, where each array contains the strings for a particular product family.

Listing Three

// Table.m for a sectioned table
#import "TableData.h"

@implementation TableData

@synthesize productNamesPhone;
@synthesize productNamesPlayer;
@synthesize productNamesLaptop;
@synthesize productNamesDesktop;
@synthesize productNamesSettop;
@synthesize productNamesTablet;
@synthesize productSections;

-(void) initProductNames {
	productNamesPhone = [[NSMutableArray alloc] initWithObjects:@"iPhone", @"iPhone 3G", @"iPhone 3GS", @"iPhone 4", nil];
	productNamesPlayer = [[NSMutableArray alloc] initWithObjects: @"iPod classic", @"iPod nano", @"iPod shuffle", @"iPod touch", nil];
	productNamesLaptop = [[NSMutableArray alloc] initWithObjects:@"Macbook", @"Macbook Pro", @"Macbook Air", nil];
	productNamesDesktop = [[NSMutableArray alloc] initWithObjects:@"iMac", @"Mac mini", @"Mac Pro", nil ]; 
	productNamesSettop = [[NSMutableArray alloc] initWithObjects:@"Apple TV", nil];
	productNamesTablet = [[NSMutableArray alloc] initWithObjects:@"iPad", nil];
	productSections = [[NSMutableArray alloc] initWithObjects:@"Phone", @"Player", @"Laptop", @"Desktop", @"Settop", @"Tablet", nil];

-(void)dealloc {
	[productNamesPhone release];
	[productNamesPlayer release];
	[productNamesLaptop release];
	[productNamesDesktop release];
	[productNamesSettop release];
	[productSections release];

	[super dealloc];

In the table view controller's viewDidLoad method, we define another array, listOfRows. Into this array we pack the product family arrays. See the second example program, SectionTable, for details.

For the numberOfSectionsInTableView method, instead of a hard-coded value of one, we now return the count of the product sections, like so:

return [products.productSections count];

The same is done for numberOfRowsInSection, except here we return the count of objects that represent the rows:

return [[listOfRows objectAtIndex:section] count];

The section parameter references the appropriate product array within listOfRows.,

Given the rearrangement of the data model, the code in cellForRowAtIndexPath must be modified as well. The code to update the cell's text content becomes:

cell.textLabel.text = [[listOfRows objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];

Finally, to improve on the display of rows in the table, we modify the table view's property separatorColor, making it a light gray. This is done in the numberOfSectionsInTableView method because it is called only once. Now when the app is built and run, the screen displays the products, partitioned into their families.

These examples only scratch the surface of what's possible with iOS tables. Feel free to edit and tinker with the example apps. Try changing the cell's style. Or with IB, change the table's property from plain to grouped and observe the results. Such experiments can help expand your knowledge of how iOS table design is done.

Table 1 lists important classes and where they're described in the SDK documentation.

[Click image to view at full size]
Table 1: Important classes

Related Reading

More Insights

Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.