The table view controller (iOS 4)

Like the plain view controller, the table view controller manages a single page. Unlike the plain view controller, it does so in a structured manner. It automatically organizes the data in a nicely formatted table.

Our discussion of the table view controller will be similar to the discussion we just completed of the bare view controller. We’ll examine its place in the view hierarchy, and then you’ll learn how to create it, modify it, and use it at runtime.

Let’s get started by examining the new view controller’s anatomy.

The anatomy of a table view controller

The table view controller’s setup is slightly more complex than that of the bare view controller. A UITableViewController controls a UITableView, which is an object that contains some number of UITableViewCell objects arranged in a single column. This is shown in figure 5.4.

By default, the controller is both the delegate and the data source of the UITableView. As we’ve previously discussed, these properties help a view hand off events and actions to its controller. The responsibilities for each of these control types are defined by a specific protocol: UITableViewDelegate declares which messages the table view controller must respond to, and UITableViewDataSource details how it must provide the table view with content. You can look up these protocols in the same library that you’ve been using for class references.


 A table view controller controls a table view and its collection of cells.

Figure 5.4 A table view controller controls a table view and its collection of cells.

Of all the view controllers, the table view controller is the trickiest to create on its own, for reasons that you’ll see momentarily.

Creating a table view controller

The easiest way to create an application that uses a table view controller is to use the Navigation-Based template in Xcode. This provides you with a delegate and a view that contains a table view controller. It also creates some of the delegate methods required for interfacing with the table view.

Although you can quickly start an application using the Navigation-Based template, we’ll discuss in detail how you can manually build a table view controller project. This will give you a better understanding of what’s going on when you use the template. Table 5.6 shows the process.

Table 5.6 Creating a table view controller is simple, but it involves several steps.

Step

Description

1.

Create a new project.

Open a Window-Based Application, and select iPhone from the Product drop-down menu.

2.

Create a table view controller.

Create a new file containing a subclass of UIViewController. Then, select

UITableViewController from the options. By default, the .xib for the view controller will be automatically created and linked. If not, you can perform step 3 manually.

3.

Link your Interface Builder object.

Create an IBOutlet for your interface in the app delegate header file.

Link an outlet from your table view controller to the IBOutlet in the app delegate object, using the Connections tab of the inspector window.

4.

Connect your controller.

Link the controller’s view to your main window.

The project-creation, object-creation, and object-linking steps pretty much follow the lessons you’ve already learned. You have to create the subclass for the table view controller because the class file is where you define what the table view contains; we’ll cover this in more depth shortly.

Note that you use two of the more advanced visual techniques that you learned in topic 4: first linking in a new class (by changing the Identity tab) and then creating a new connection from it to your app delegate (via the Connections tab). As a result, you end up with two connections. On the one hand, the table view controller depends on your RootViewController files for its own methods; on the other hand, your app delegate file links to the controller (and eventually to the methods) via its outlet. This two-part connection is common, and you should make sure you understand it before moving on.

As usual, you could elect to create this object programmatically, by using an alloc-init command:

tmp12119_thumb

The following simple code finishes the table-creation process by linking in the table’s view in step 4 of the process:

tmp12120_thumb

Note that you link up your table view controller’s view—not the controller itself—to your window. You’ve seen in the past that view controllers come with automatically created views. Here, the view is a table view.

If you want to see how that table view works, you can now click the table view to get its details. As shown in figure 5.5, it already has connections created for its dataSource and delegate properties.

Next, you need to fill the table with content.

A look at the connections automatically created for a controller's table view

Figure 5.5 A look at the connections automatically created for a controller’s table view

Building up a table interface

As the data source, the controller needs to provide the view with its content. This is why you created a subclass for your table view controller and why every one of your table view controllers should have its own subclass: each will need to fill in its data in a different way.

We’ve mentioned that the UITableViewDataSource protocol declares the methods your table view controller should pay attention to in order to correctly act as the data source. The main work of filling in a table is done by the tableView:cellForRowAt-IndexPath: method. When passed a row number, this method should return the UITableViewCell for that row of your table.

Before you can get to that method, though, you need to do some work. First, you must define the content that will fill your table. Then, you must define how large the table will be. Only then can you fill in the table using the tableView:cellForRowAt-IndexPath: method.

In addition to these major table view elements, we’ll also cover two optional variants that can change how a table looks: accessory views and sections.

CREATING THE CONTENT

You can use numerous SDK objects to create a list of data that your table should contain. In topic 9, we’ll talk about SQLite databases; and in topic 14, we’ll discuss pulling RSS data off the Internet. For now, we stay with the SDK’s simpler objects. The most obvious are NSArray, which produces a static indexed array; NSMutableArray, which creates a dynamic indexed array; and NSDictionary, which defines an associative array.

For this example of table view content creation, you’ll create an NSArray containing an NSDictionary that itself contains color names and UIColor values. As you can probably guess, you’ll fill this skeletal table view example with something like the color selector that you wrote back when you were learning about views in topic 4. The code required to create your content array is shown in the following listing.

Listing 5.2 Create the content array

Listing 5.2 Create the content array

You should do this sort of setup before the view appears. Here, you do it in the view-DidLoad method. This method is called prior to the view appearing and is a good place to do your initialization.

The array and dictionary creations are simple. The Apple class references contain complete information about how to create and manipulate these objects; but, in short, you can create an NSArray as a listing of objects ending in a nil, and you can create an NSDictionary using pairs of values and keys, ending in a nil. Here, you’re creating an array containing four dictionaries, each of which will fill one line of your table.

You also have to think about memory management here. Because your array was created with a class factory method, it’ll be released when it goes out of scope. In order to use this array elsewhere in your class, you not only need to have defined it in your header file, but you also need to send it a retain message to keep it around. You’ll release it in your dealloc method, elsewhere in the class files.

BUILDING YOUR TABLE CELLS

When you’ve set up a data backend for your table, you need to edit three methods in your table view controller file: two that define the table and one that fills it, as shown in the following listing. We’ll explain each of these in turn.

Listing 5.3 Three methods that control how your table is created and runs

Listing 5.3 Three methods that control how your table is created and runs

All these methods should appear by default in the table view controller subclass you create, but you may need to make changes to some of them to accommodate the specifics of your table.

The first method is numberOfSectionsInTableView:. Tables can optionally include multiple sections, each of which has its own index of rows, and each of which can have a header and a footer. For this example, you’re creating a table with one section, but we’ll look at multiple sections before we finish this topic.

The second method, tableView:numberOfRowsInSection:, reports the number of rows in this section. Here, you return the size of the array you created. Note that you ignore the section variable because you have only one section.

The third method, tableView:cellForRowAtIndexPath:, takes the table set up by the previous two methods and fills its cells one at a time. Although this chunk of code looks intimidating, most of it will be sitting there waiting for you the first time you work with a table. In particular, the creation of UITableViewCell will be built in. All you need to do is set the values of the cell before it’s returned. Here you use your NSDictionary to set the cell’s text color and text content O.

Also note that this is your first use of the NSIndexPath data class. It encapsulates information on rows and sections. Cells have two views that you can access. The first is the textLabel. As you saw, this contains the text displayed in the cell. The other is imageView. It’s basically an icon for the cell. You can set this to an image view. See section 11.2 for more information about using UIImage.

You may want to change more than text content and color. Table 5.7 lists all the cell label features that you may want to experiment with at this point.

Table 5.7 You can modify your table cells in a variety of ways.

Property

Summary

textLabel.font

Sets the cell label’s font using UIFont

textLabel.lineBreakMode

Sets how the cell label’s text wraps using

UILineBreakMode

textLabel.text

Sets the content of a cell label to an NSString

textLabel.textAlignment

Sets the alignment of a cell’s label text using the

UITextAlignment constant

textLabel.textColor

Sets the color of the cell’s label text using UIColor

textLabel.selectedTextColor

Sets the color of selected text using UIColor

imageView.image

Sets the content of a cell’s imageView to a UIImage

imageView.selectedImage

Sets the content of a selected cell to UIImage

Using these properties, you can make each table cell look unique, depending on the needs of your program.

ADDING ACCESSORY VIEWS

Although you didn’t do so in the color-selector example, you can optionally set accessories on cells. Accessories are special elements that appear to the right of each list item.

Most frequently, you’ll set accessories using an accessoryType constant that has four possible values, as shown in table 5.8.

Table 5.8 A cell accessory gives additional information.

Constant

Summary

UITableViewCellAccessoryNone

No accessory

UITableViewCellAccessoryDisclosureIndicator

A normal chevron: >

UITableViewCellAccessoryDetailDisclosureButton

tmp12-124

UITableViewCellAccessoryCheckmark

tmp12-125

An accessory can be set as a property of a cell:

tmp12126_thumb

The normal chevron is usually used with a navigation controller, the blue chevron is typically used for configuration, and the checkmark indicates selection.

There is also an accessoryView property, which lets you undertake the more complex task of creating an entirely new view to the right of each list item. You create a view and then set accessoryView to that view:

tmp12127_thumb

There’s an example of this in topic 8, where you’ll be working with preference tables.

ADDING SECTIONS

The example shows how to display a single section’s worth of cells, but it would be trivial to rewrite the functions to offer different outputs for different sections within the table. Because of Objective-C’s ease of accessing nested objects, you can prepare for this by nesting an array for each section inside a larger array:

tmp12128_thumb

Then, you return the count from this uber-array for the numberOfSections: method:

tmp12129_thumb

You similarly return a subcount of one of the subarrays for the tableView:numberOf-Rows: method:

tmp12130_thumb

Finally, you pull content from the appropriate subarray when filling in your cells using the same type of nested messaging.

When you’re working with sections, you can also think about creating headers and footers for each section. Figure 5.6 shows what the revised application looks like so far, including two different sections, each of which has its own section header.

How do you create those section headers? As with all the methods you’ve seen that fill in table views, the section header messages and properties show up in the UITableViewDataSource protocol reference.

To create section headers, you write a tableView:titleForHeaderInSection: method. As you’d expect, it renders a header for each individual section.

An example of its use is shown here. You could probably do something fancier instead, such as building the section names directly into your array:

tmp12131_thumb

 

 

Section headers can improve the usability of table views. Here they're shown in use on both the iPad and iPhone.

Figure 5.6 Section headers can improve the usability of table views. Here they’re shown in use on both the iPad and iPhone.

You can similarly set footers and otherwise manipulate sections according to the protocol reference.

There’s still more to the table view controller. Not only do you have to work with data when you’re setting it up, but you also have to do so when it’s in active use, which usually occurs when the user selects individual cells.

Using your table view controller

We won’t dwell too much on the more dynamic possibilities of the UITableView-Controller here. For the most part, you’ll either use it to hold relatively static data (as you do here) or use it to interact with a navigation controller (as you’ll see in topic 7). But before we finish up with table view controllers, we’ll look at one other fundamental: selection.

SELECTED CELLS

If you try the sample table view application that you’ve been building throughout section 5.3, you’ll see that individual elements in a table view can be selected.

In table 5.7, you saw that some properties apply explicitly to selected cells. For example, the following maintains the color of your text when it’s selected, rather than changing it to white, as per the default:

tmp12133_thumb

To set this value, you must add this line of code to your tableView:didSelectRowAt-IndexPath: method. Also note that this is another example of using nested arrays to provide section- and row-specific information for a table list.

The tableView:didSelectRowAtIndexPath: method is the most important for dealing with selections. This method appears in the UITableViewDelegate protocol and tells you when a row has been selected. The message includes an index path, which, as you’ve already seen, contains both a row and a section number.

Here’s a simple example of how you might use this method to checkmark items in your list:

tmp12134_thumb

You can easily retrieve the selected cell by using the index path, and then you use that information to set the accessory value. You’ll make more use of cell selection in topic 7, when we talk about navigation controllers.

Summary

View controllers are the most important building blocks of the iOS SDK that you hadn’t seen up to this point. As we explained in this topic, they sit atop views of all sorts and control how those views work. Even in this topic’s simple examples, you saw some real-world examples of this control, as view controllers managed rotation, filled tables, and reacted to selections.

You can think of a view controller as being like the glue of your application. It connects your view components to the underlying models. View controllers provide interaction with the interface through IBOutlets and IBActions.

Now that we’re getting into user interaction, we’re ready to examine how it works in more depth, and that’s the focus of the next topic. We’ll examine the underpinnings of user interaction: events and actions.

Next post:

Previous post: