iPhone Application Development : Displaying and Navigating Data Using Table Views - Building a Simple Table View Application

4/27/2011 6:06:25 PM
When the user touches an entry in the table, an alert will appear showing the chosen flower name and color. The final application will look similar to Figure 1.
Figure 1. In our first application, we’ll create a table that can react to a user’s interactions.

Implementation Overview

A table view is created by an instance of UITableView and classes that implement the UITableViewDataSource and UITableViewDelegate protocols to provide the data that the table will display. Specifically, the data source needs to supply information on the number of sections in the table and the number of rows in each section. The delegate handles creating the cells within the table and reacting to the user’s selection.

Unlike the picker, where we implemented the required protocols within a standard UIViewController, we’ll be creating an instance of UITableViewController, which conforms to both of the needed protocols. This simplifies our development and keeps things as streamlined as possible. In a large project with complex data needs, you may want to create one or more new classes specifically for handling the data.

Preparing the Project

Begin by creating a new Xcode project named FlowerColorTable using the Window-Based iPhone Application template.

We will need a new subclass of the UITableViewController class to handle interactions with our table view. To add the new class, complete the following steps:

Create a new file in Xcode (File, New File).

Within the New File dialog box, select Cocoa Touch Class, UIViewController subclass, and then check the UITableViewController subclass, as seen in Figure 2. Note that we don’t need a separate XIB file because we’ll be generating the content of table view programmatically.

Figure 2. Create the files for implementing a new subclass of UITableViewController.

Click Next.

Type FlowerColorTableViewController.m as the filename, and be sure that Also Create FlowerColorTableViewController.h is selected.

Finally, click the Finish button. The new subclass will be added to your project.

The FlowerColorTableViewController.m will include all the method stubs you need to get your table up and running quickly. In a few minutes, we’ll add an instance of this new table view controller subclass to the MainWindow.xib file. This will create an instance of the FlowerColorTableViewController when the application launches.

Adding Outlets

Before we can make our connections in Interface Builder, we need to create an outlet for the application delegate to access the FlowerColorTableViewController that we’re adding to the system.

Edit FlowerColorTableAppDelegate.h and add a line that imports the FlowerColorTableViewController interface file and an outlet for the instance of FlowerViewController that we will be creating; we’ll call it flowerColorTableViewController for consistency.

Did you Know?

Why do we import the FlowerColorTableViewController.h file? If we didn’t, Xcode wouldn’t “know” what a FlowerColorTableViewController is, and we wouldn’t be able to declare an instance of it.

The FlowerColorTableAppDelegate.h code should read as shown in Listing 1.

Listing 1.
 1: #import <UIKit/UIKit.h>
2: #import "FlowerColorTableViewController.h";
4: @interface FlowerColorTableAppDelegate : NSObject <UIApplicationDelegate> {
5: IBOutlet FlowerColorTableViewController *flowerColorTableViewController;
6: UIWindow *window;
7: }
9: @property (nonatomic, retain) IBOutlet UIWindow *window;
11: @end

Lines 2 and 5 are the only additions to the app delegate interface file.

Adding the View

After the view controller has been instantiated, it will need to add its subview to the application window. Make these implementation changes within the FlowerColorTableAppDelegate class.

Start by editing application:didFinishLaunchingWithOptions, using the addSubview method to add the add the flowerColorTableViewController’s view as a subview to the window:

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// Override point for customization after application launch
[window addSubview:flowerColorTableViewController.view];
[window makeKeyAndVisible];

return YES;

Next, release the table view controller (flowerColorTableViewController) when the application is finished. Edit the dealloc method to read as follows:

- (void)dealloc {
[flowerColorTableViewController release];
[window release];
[super dealloc];

Although we’ve already added references to a table view controller (flowerColorTableViewController) in our code, we haven’t created the object yet. It’s time to open Interface Builder and add an instance of our class.

Adding a Table View and Table View Controller Instance

Double-click the MainWindow.xib file to open it within Interface Builder. Open the Library, and drag the Table View Controller icon into the MainWindow.xib file. Double-click the table view controller within the Interface Builder Document window (Window, Documents) to show what a sample UITable looks like in Interface Builder, as shown in Figure 3.

Figure 3. The sample UITable shown by Interface Builder.

Okay, so we’ve added an instance of UITableViewController to the project, but that’s not quite what we need! The FlowerColorTableViewController class is our subclass of UITableViewController, so that’s what we want to instantiate and use in the application.

By the Way

The UITableViewController instance that you just added includes an instance of a table view (UITableView), so this is the only object needed in Interface Builder.

Select the Table View Controller icon in the XIB file, and open the Identity Inspector (Command+4). Edit the class identity to read FlowerColorTableViewController, as shown in Figure 4.

Figure 4. Update the class identity to FlowerColorTableViewController within the Identity Inspector.

The application will now instantiate our table view controller when it launches, but the controller still isn’t connected to anything. To connect to the flowerColorTableViewController outlet created earlier, Control-drag from Flower Color Table App Delegate to the Flower Color Table View Controller icon. When the Outlets pop-up window appears, choose flowerColorTableViewController (see Figure 5).

Figure 5. Connect the application delegate to the flowerColorTableViewController.

All the connections are in place for the table view controller and a table view. Switch back to Xcode, and click Build and Go to test the application. An empty table will appear. It’s empty, but it’s a table! Next step? Data!

Providing Data to the Table View

With all the structural work out of the way, the table is ready to display something. As mentioned earlier, implementing the required methods for the UITableViewDataSource and UITableViewDelegate protocols is very similar to creating a picker. For this example, we create a table that lists flowers divided into sections by color.

Creating Sample Data

To keep things simple, we’ll only consider two colors: red and blue. We’ll populate two arrays (redFlowers, blueFlowers) with a few appropriate flower names.

Begin by updating FlowerColorTableViewController.h to include the two NSMutableArrays we’ll be using:

@interface FlowerColorTableViewController : UITableViewController {
NSMutableArray *redFlowers;
NSMutableArray *blueFlowers;

Turning to the implementation in FlowerColorTableViewController.m, find the viewDidLoad method and uncomment it. We will implement this method so that we have a convenient place to populate the arrays. Add the code in Listing 2 to initialize of the redFlowers and blueFlowers arrays with several flowers in each.

Listing 2.
- (void)viewDidLoad {
[super viewDidLoad];
redFlowers = [[NSMutableArray alloc]
blueFlowers = [[NSMutableArray alloc]
,@"Sea Holly",@"Phlox",@"Iris",@"Bluebell"

As always, make sure that you release the two arrays when finished. Edit the dealloc method to read as follows:

- (void)dealloc {
[redFlowers release];
[blueFlowers release];
[super dealloc];

As a last step, add constants that we can use to refer to our color sections. At the top of FlowerColorTableViewController.m, enter the following constant definitions:

#define sectionCount 2
#define redSection 0
#define blueSection 1

The first constant, sectionCount, is the number of sections that will be displayed in the table. Because we’re implementing red and blue flower lists, this value is 2. The next constant, redSection, denotes that the listing of red flowers in the table will be shown first (section 0), while the third and final constant, blueSection, identifies that the blue section of flowers will appear second (section 1).

Implementing the Table View Controller Data Source Methods

Our application now has the data it needs to create a table, but it doesn’t yet “understand” how to get that data into the table view itself. Thankfully, the methods that a table requires to display information are easy to understand and, more important, easy to implement. Because this example includes sections (red and blue), we need to include these three methods in FlowerColorViewController.m as part of the UITableViewDataSource protocol:

numberofSectionsInTableView: Returns the number of sections within a given table

tableView:tableViewnumberOfRowsInSection: Returns the number of rows in a section

tableView:titleForHeaderInSection: Returns a string to be used as the title for a given section number

The number of sections has already been defined in the constant sectionCount, so implementing the first method requires nothing more than returning this constant. Add this code to FlowerColorViewController.m:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
return sectionCount;

The second method requires us to return the number of rows (cells) that will be displayed in a given section. Because the rows in each section will be filled with the strings in the redFlowers and blueFlowers arrays, we can return the count of elements in each array using the array count method.

Use a switch statement along with the redSection and blueSection constants that were defined earlier to return the appropriate counts for each of the two arrays. The final implementation is shown in Listing 3. Be sure to add this to FlowerColorViewController.m.

Listing 3.
- (NSInteger)tableView:(UITableView *)tableView
switch (section) {
case redSection:
return [redFlowers count];
case blueSection:
return [blueFlowers count];
return 0;

By the Way

Even though it is impossible for our application to reach a section other than red or blue, it is still good practice to provide a default case to the switch statement. This ensures that even if we haven’t properly identified all of our potential cases, it will still be caught by the default case.

For the third data source method, tableView:titleForHeaderInSection, you can turn again to the defined constants and a switch statement to very easily return an appropriate string for a given section number. Implement the method as shown in Listing 4.

Listing 4.
- (NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section {
switch (section) {
case redSection:
return @"Red";
case blueSection:
return @"Blue";
return @"Unknown";

That wraps up what needs to be provided to satisfy the UITableViewDataSource protocol. However, as you’ve seen, these methods don’t provide the actual data that will be visible in the table cells.

Populating the Cells

At long last, we’ve reached the method that actually makes our table display something! Both tableView:cellForRowAtIndexPath will do the “heavy lifting” in the application. This single method, which needs to implemented within your table view controller, returns a cell (UITableViewCell) object for a given table section and row.

By the Way

The methods required for working with table views frequently use an NSIndexPath object to communicate row and section information. When dealing with an incoming NSIndexPath object in your table methods, you can use the accessors IndexPath.section and IndexPath.row to get to the current section and row.

To implement the tableView:cellForRowAtIndexPath method properly, we must create and return a properly formatted UITableViewCell.

What makes this process interesting is that as cells move on and off the screen, we don’t want to keep releasing and reallocating memory. We also don’t want to allocate memory for every single cell that the table could display. So, what is the alternative? To reuse cells that are no longer needed to generate the current display. The good news is that Apple has already set up methods and a process for this to occur automatically.

Take a close look at the method stub for tableView:cellForRowIndexPath; specifically, this snippet:

UITableViewCell *cell = (UITableViewCell*)[tableView
if(cell == nil)
cell = [[[UITableViewCell alloc]
reuseIdentifier:CellIdentifier] autorelease];

This code attempts to use the dequeueReusableCellWithIdentifier UITableView method to find a cell that has already been allocated but that is ready to be reused. In the event that an appropriate cell can’t be found (such as the first time the table view loads its cells), a new cell is allocated and initialized. You shouldn’t have any reason to change this prewritten logic for most table-based applications.

After a cell object has been appropriately allocated, the method must format the cell for the indexPath object provided. In other words, we must make sure that for whatever section is specified in indexPath.section and whatever row is passed in indexPath.row, the cell object is given the necessary label.

To set a cell’s label to a given string, first use textLabel to return the UILabel object for the cell, then the setText method to update the label. For example:

[[cell textLabel]setText: @"My Cell Label"]

Because we don’t want to set a static string for the cell labels and our labels are stored in arrays, we need to retrieve the appropriate label string from the array, and then pass it to setText. Remember that the individual cell row that we need to return will be provided by indexPath.row, so we can use that to index into our array. To retrieve the current text label for a member of the redFlowers array, we can use the following:

[redFlowers objectAtIndex:indexPath.row]

These two lines can be combined into a single statement that sets the cell label text to the current row of the redFlowers array:

[[cell textLabel] setText:[redFlowers objectAtIndex:indexPath.row]]

This is good, but not quite the solution to all our problems. We need to account for both the redFlowers and blueFlowers arrays and display each within the appropriate section. Once again, we’ll turn to the switch statement to make this happen, this time using indexPath.section to determine whether the cell should be set to a member of the redFlowers array or the blueFlowers array.

Your final code, shown in Listing 13.5, should be an addition to the existing tableView:cellForRowAtIndexPath method stub.

Listing 5.
 1: - (UITableViewCell *)tableView:(UITableView *)tableView
2: cellForRowAtIndexPath:(NSIndexPath *)indexPath
3: {
4: static NSString *CellIdentifier = @"Cell";
6: UITableViewCell *cell = (UITableViewCell*)[tableView
7: dequeueReusableCellWithIdentifier:CellIdentifier];
8: if(cell == nil)
9: {
10: cell = [[[UITableViewCell alloc]
11: initWithFrame:CGRectZero
12: reuseIdentifier:CellIdentifier] autorelease];
13: }
15: switch (indexPath.section) {
16: case redSection:
17: [[cell textLabel]
18: setText:[redFlowers objectAtIndex:indexPath.row]];
19: break;
20: case blueSection:
21: [[cell textLabel]
22: setText:[blueFlowers objectAtIndex:indexPath.row]];
23: break;
24: default:
25: [[cell textLabel]
26: setText:@"Unknown"];
27: }
28: return cell;
29: }

The moment you’ve been waiting for has arrived! You should now be able to launch your application and view the result. Congratulations, you’ve just implemented a table view from scratch!

Reacting to a Row Touch Event

A table that displays information is all fine and dandy, but it would be nice if the user had a means of interacting with it. Unlike other UI elements where we’d need to define an action and make connections in Interface Builders, we can add some basic interactivity to the FlowerColorTable application by implementing a method from the UITableViewDelegate: tableView:didSelectRowAtIndexPath. This method is called when a table row has been touched by the user. The key to identifying the specific row and section that was selected is indexPath, an instance of NSIndexPath.

How you react to a row selection event is up to you. For the sake of this example, we’re going to use UIAlertView to display a message. The implementation, shown in Listing 6, should look very familiar by this point. Add this delegate method to the FlowerColorTableViewController.m file:

Listing 6.
 2:         didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
3: UIAlertView *showSelection;
4: NSString *flowerMessage;
5: switch (indexPath.section) {
6: case redSection:
7: flowerMessage=[[NSString alloc]
8: initWithFormat:
9: @"You chose the red flower - %@",
10: [redFlowers objectAtIndex: indexPath.row]];
11: break;
12: case blueSection:
13: flowerMessage=[[NSString alloc]
14: initWithFormat:
15: @"You chose the blue flower - %@",
16: [blueFlowers objectAtIndex: indexPath.row]];
17: break;
18: default:
19: flowerMessage=[[NSString alloc]
20: initWithFormat:
21: @"I have no idea what you chose!?"];
22: break;
23: }
25: showSelection = [[UIAlertView alloc]
26: initWithTitle: @"Flower Selected"
27: message:flowerMessage
28: delegate: nil
29: cancelButtonTitle: @"Ok"
30: otherButtonTitles: nil];
31: [showSelection show];
32: [showSelection release];
33: [flowerMessage release];
34: }

Lines 3–4 declare flowerMessage and showSelection variables that will be used for the message string shown to the user and the UIAlertView instance that will display the message, respectively.

Lines 5–23 use a switch statement with indexPath.section to determine which flower array our selection comes from and the indexPath.row value to identify the specific element of the array that was chosen. A string (flowerMessage) is allocated and formatted to contain the value of the selection.

Lines 25–31 create and display an alert view instance (showSelection) containing the message string (flowerMessage).

Lines 32–33 release the instance of the alert view and the message string.

After implementing this function, build and test the application again. Touch a row, and review the result. The application will now display an alert box with the results of your selection, as shown in Figure 6.

Figure 6. The application now reacts to row selection and identifies the item that was selected.

  •  iPhone Application Development : Understanding Table Views and Navigation Controllers
  •  Windows Phone 7 Development : Revising WeatherRx to Manage Slow Data Connections
  •  Windows Phone 7 Development : Handling Data Connection Issues with Rx.NET
  •  Windows Phone 7 Development : Handling Errors in Rx.NET
  •  Windows Phone 7 Development : Using Rx.NET with Web Services to Asynchronously Retrieve Weather Data
  •  Windows Phone 7 Development : Media - Adding Sounds to an Application
  •  iPhone Application Development : Building a Multi-View Tab Bar Application (part 4) - Implementing the Summary View
  •  iPhone Application Development : Building a Multi-View Tab Bar Application (part 3) - Implementing the Volume View
  •  iPhone Application Development : Building a Multi-View Tab Bar Application (part 2) - Implementing the Area View
  •  iPhone Application Development : Building a Multi-View Tab Bar Application (part 1)
  •  Windows Phone 7 Development : Working with Video (part 2) - Coding the Application
  •  Windows Phone 7 Development : Working with Video (part 1)
  •  Windows Phone 7 Development : Plotting an Address on a Bing Maps Map and Working with the Bing Maps Service
  •  Windows Phone 7 Development : Using GeoCoordinateWatcher and the Bing Maps Control to Track Your Movements
  •  iPhone Application Development : Creating a Multi-View Toolbar Application (part 3) - Adding Toolbar Controls
  •  iPhone Application Development : Creating a Multi-View Toolbar Application (part 2) - Instantiating the View Controllers
  •  iPhone Application Development : Creating a Multi-View Toolbar Application (part 1)
  •  Windows Phone 7 Development : Using Location Services - Simulating the Location Service
  •  Introducing the Windows Phone Location Service and Mapping APIs
  •  iPhone Application Development : Implementing a Custom Picker View (part 4) - Tweaking the Picker UI
    Top 10
    Get to a SharePoint Site
    Designing and Configuring Unified Messaging in Exchange Server 2010 : Unified Messaging Architecture (part 1)
    Optimizing an Exchange Server 2010 Environment - Analyzing and Monitoring Core Elements
    Hacking - The Whole Infrastructure
    Windows Server 2008 : Understanding Internet Information Services (IIS) 7.5
    Using and Configuring Public Folder Sharing
    Windows Server 2008 : Installing and Upgrading IIS 7.5
    SQL Server 2008 : Working with DML Queries - Using the MERGE Statement
    Communicate Between Two Machines on the Same Network (WCF)
    OData with SQL Azure - Enabling OData on an Azure Database
    Most View
    Windows Azure : Processing with worker roles - Communicating with a worker role
    Programming the Mobile Web : Widgets and Offline Webapps - Platforms (part 3) - webOS & Android
    Transact-SQL in SQL Server 2008 : MERGE Statement
    iPhone 3D Programming : Blending and Augmented Reality - Shifting Texture Color with Per-Vertex Color
    Miscs Apps for iOS Device (April 2012) : SleepKeeper, Cristiano Ronaldo, NatureTap
    Speed ​​up browsing with a faster DNS
    Windows 7 : Working with User Accounts (part 1)
    Reporting Services with SQL Azure : Deploying the Report & Creating a Subreport
    Exploring the T-SQL Enhancements in SQL Server 2005 : TOP Enhancements
    SharePoint Administration with PowerShell (part 1)
    Share and stream media (Part 2) - Stream away to an Android device
    Searching for Google’s future (Part 1) - Taking the tablets
    Create virtual desktop with nSpaces (Part 1)
    Seagate GoFlex Satellite - Pocket Satellite
    Programming Microsoft SQL Serve 2005 : An Overview of SQL CLR - CLR Aggregates
    Application Security in Windows Vista
    Design and Deploy High Availability for Exchange 2007 : Design Hub Transport High Availability
    Think the Brighter Side to Piracy
    Build Up Your Dream House with PC (Part 4)
    Algorithms for Compiler Design: PEEPHOLE OPTIMIZATION