Channels ▼

Embedded Systems

Displaying Tabular Data on iPhones

Table Classes

A key class for managing an iOS device screen is the UIView. It is responsible for painting content onto the screen and capturing user events (typically touches)t. I have discussed the UIView class and shown how to use it in the DDJ article, "The iPhone Isn't Easy", DDJ March 27, 2010.

I mention the UIView class here because UITableView and UITableViewCell subclass it. Therefore, they inherit the ability to display content and respond to touch events. However, these classes do not store data. They only display the data and enable the user to interact with the app's data repository (which is known as the data model).

UITableView is responsible for implementing a table's layout and certain behaviors. It uses cell objects (described next) to organize and display the data. As Figure 2 shows, UITableView also inherits from UIScrollView, which allows the table to be scrolled. However, the table can scroll vertically only.

[Click image to view at full size]
Figure 2: How the Cocoa Touch objects and protocols interact to lay out a table.

The UITableViewCell class is responsible for displaying a row and its content. This class serves as a template that clones as many instances of a cell as is required to make up a section. In this sense, UITableViewCell functions similarly to a Java factory method. Each cell can have a different style and displays the information that it references in the data model. As mentioned earlier, a cell can display text, images, or controls. Cells are complex objects with many built-in views, including several content-specific views, a background view, and an accessory view. Figure 3 illustrates this. The accessory view displays controls or optional images that assist in navigating to another screen.

[Click image to view at full size]
Figure 3: Some of the key views in a cell using the default style.

Table Protocols

In iOS parlance, a protocol defines method declarations and the desired responses when these methods are invoked. It's up to the developer to assign a delegate object that implements the protocol. An Objective-C protocol therefore functions like a Java interface. The iOS runtime is made aware that a class is a delegate object by the addition of a suitable protocol declaration in the class's header file.

Tables use two protocols: UITableViewDataSource_Protocol, and UITableViewDelegate_Protocol. The UITableViewDataSource_Protocol obtains information for UITableView about table layout and how to supply data to it for display. For table layout, this protocol defines two required methods. The first, numberOfRowsInSection, queries the app about the number of rows a section has. The second method, cellForRowAtIndexPath, retrieves a specific cell in the table so that it can be matched up to its data source and the data displayed.

The UITableViewDelegate_Protocol describes how the table behaves and its appearance. This protocol is employed to customize the appearance of headers and footers, modify a cell's height, and specify a background color or image. All methods in this protocol are optional.

A UITableViewController class is a variant of the NUIViewController class that's designed to manage a table. It packages housekeeping functions and provides the stub routines for the data source and delegate protocol methods into one convenient view controller class. This design allows the table's event handling, data display, data updates, and custom behavior to be contained in one omnibus object. UITableViewController also performs duties such as loading the table's data, flashing the scroll indicators, and clearing the selection choice. It exposes a property, tableview, which allows access to the table view's elements and properties.

A final class,NSIndexPath, which has little to do with tables but it is crucial to their operation, describes a path to a node in a tree of arrays. Recall that tables are organized as a hierarchy of lists, where each list corresponds to an array. For tables, an instance of NSIndexPath provides the section and row information that references a specific cell, and can be used to also reference its data. Many table view methods described here use NSIndexPath instances as arguments, or they return results as an NSIndexPath.

For more information on each class, consult the iOS SDK documentation.

Making a Simple Table

Let's write some code. The first decision is whether to use a NUIViewController to manage the instance of UITableView, or a UITableViewController. The NUIViewController choice gives you fine-grained control over every aspect of the table's appearance and operation. However, such flexibility comes at a price: You have to manage every detail of the table's operation, such as assigning and implementing the protocol delegate objects. In contrast, UITableViewController manages a lot of these details for you because it assigns itself as the delegate object for both protocols, and provides some housekeeping routines. The tradeoff is that some flexibility in table design is lost because this class implements certain default behaviors. However, I have used UITableViewControllers for data display and in many cases I was able to customize the tables' appearance without too much effort. Unless you require an extremely customized UI, I would recommend starting with a UITableViewController. The example apps use UITableViewController for table management, so that is the class I'll discuss. We'll start out simply by presenting a list of existing Apple products.

From within Xcode, Apple's IDE for writing and debugging Mac OS X and iOS apps, choose File > New Project. In the New Project window, choose iPhone OS Application and Window-based Application for the template. These choices will generate a minimalist app framework where you can add our own code and observe the behavior. Click Choose, name the project TestTable or something similar, and click Save. Xcode generates a basic iOS app template that you can build and run. All the app displays at this point is a blank screen, but that's fine. We're going to add the table as we go, and if something breaks along the way, we'll know where.

Now choose File > New File. In the dialog, select Cocoa Touch Class, and NUIViewController subclass for the file's code template. Under the options, make sure that UITableViewController subclass is checked, and that With XIB for user interface is unchecked. This choice directs Xcode to add a UITableViewController class to the app's template. Since we aren't including a nib file, we'll use Interface Builder to add the UI elements later. Click Next, and name the subclass file something descriptive like Table.m. When you build the app this time, two errors appear.

These errors flag the data source delegate methods numberOfSectionsInTableView and numberOfRowsInSection. Xcode has provided stub routines for these data source protocols in the code template, but it can only go far: you must supply information about the table layout. As mentioned previously, numberOfRowsInSection is a required method that requests the number of rows in a section. Oddly, numberOfSectionsInTableView, which asks for the number of sections in the table, is not. However, if you fail to provide a value greater than zero for the section number, the app crashes. For simple tables, you can hard-code these values, but for greater flexibility their values should be derived from the app's data model. That way when the data model changes, so do the tables. Leave these alone for now, since we need to create some data to display.

Make a new file with Xcode, but this time specify an Objective-C class. For the "Subclass of" option, choose NSObject. Name this file TableData.m. Create a data array productNames, and add data strings to it, similar to that in Listing One.

Listing One

// TableData.m for a plain table 
#import "TableData.h"

@implementation TableData
@synthesize productNames;

-(void) initProductNames {
	productNames = [[NSMutableArray alloc] initWithObjects:@"iPhone", @"iPhone 3G", @"iPhone 3GS", @"iPhone 4",
	   @"iPod classic", @"iPod nano", @"iPod shuffle", @"iPod touch",
	   @"Macbook", @"Macbook Pro", @"Macbook Air",
	   @"iMac", @"Mac mini", @"Mac Pro", 
	   @"Apple TV", @"iPad", nil];

-(void)dealloc {	
	[productNames release];
	[super dealloc];	

Be sure to add a declaration of productNames in the TableData.h file and also for the function initProductNames. Since we're working with constant data, we could use an instance of NSArray rather than NSMutableArray. However, for a real app, you want to allocate mutable data arrays so that you can buffer and process sensor information or download data over the air into them before displaying their contents.

Now go back to Table.m, uncomment the method viewDidLoad and add code to initialize an instance of TableData called products and to invoke initProductNames. Edit numberOfSectionsInTableView to return a value of one, and numberOfRowsInSection to return a count value for the number of elements in the productNames array. See the example app project PlainTable for more detail. If all goes well, the app should build without errors and run. However, nothing appears on the screen.

Related Reading

More Insights

Currently we allow the following HTML tags in comments:

Single tags


<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags


<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

