MOBILE

iPhone Application Development : Creating a Navigation-Based Application

5/10/2011 3:45:26 PM
This tutorial will implement an app that displays a list of flowers by color, including images for each row. It will also enable the user to touch a specific flower and show a detail view. The detail view will load the content of a Wikipedia article for the selected flower. The finished application will resemble Figure 1.
Figure 1. Our navigation-based application will display flowers, including thumbnails and details about specific flower types.

Implementation Overview

The implementation of a navigation-based application is refreshingly simple. As a developer, the navigation controller (UINavigationController) frees you up to focus on writing application functionality. When you want a new view to appear, you simply “push” its view controller onto the navigation controller’s stack. The new controller is instantiated and added to the stack, and the previous controller gets pushed further down the stack. When (and if) it is time to go back, the navigation controller “pops” the current view off the stack, unloading it. The previous view controller then moves to the top of the stack, becomes active again, and the user can navigate to another item.

To manage the data, we’ll use a combination of NSMutableDictionaries and NSMutableArrays to store our data in a more easy-to-maintain format.


If you haven’t encountered stacks before, don’t worry; you’ll catch on quickly. Imagine creating a stack of papers on your desk. To add a page to the stack, you “push” it onto the stack. You may only remove the top page at any time, by “popping” it off. The more pages you push onto the stack of paper, the more you’ll have to “pop” off to get back to the first page.

A navigation controller does exactly this but with view controllers/views rather than pieces of paper.


Preparing the Project

Instead of starting with the Window-Based Application template, start Xcode and create a new project using the Navigation-Based Application template. If you want to follow along exactly with what we’re doing, name the project FlowerInfoNavigator.

The Navigation-Based Application template does all the hard work of setting up a navigation controller and an initial table-based view. This is the “heart and soul” of many navigation-based applications and gives us a great starting point for adding functionality.

Understanding the UINavigationController Hierarchy

After creating the new project, click the Classes folder and review the contents. You should see header and implementation files for the application delegate (FlowerInfoNavigatorAppDelegate) and a subclass of UITableViewController called RootViewController. We will supplement this by creating a new detail view controller shortly.

Exploring the XIB files reveals an interesting hierarchy, as shown in Figure 2.

Figure 2 The MainWindow.xib contains a navigation controller along with a table controller for the root-level table view.

The MainWindow.xib file contains all the usual components but also a navigation controller (UINavigationController) with navigation bar (UINavigationBar). The controller provides the functionality to push and pop other view controllers, while the UINavigationBar instance creates the horizontal bar that will contain our UI elements for navigating through views.

Inside the navigation controller is the instance of the Root View Controller (a subclass of UITableViewController). This is the top-level controller that is pushed onto the navigation controller. A user cannot navigate back beyond this controller. (Note that the table view itself is loaded RootViewController.xib.)

Finally, within the Root View Controller is a navigation item (UINavigationItem), which we will use to display a title in the navigation bar.

Feel free to build the app and try it out. Even though we’re starting with an empty template, you’ll still be able to see the navigation bar and the root table view.

Providing Data to the Application

In the previous table implementation project, we used multiple arrays and switch statements to differentiate between the different sections of flowers. This time, however, we need to track the flower sections, names, image resources, and the detail URL that will be displayed.

Creating the Application Data Structures

What the application needs to store is quite a bit of data for simple arrays. Instead, we’ll make use of an NSMutableArray of NSMutableDictionaries to hold the specific attributes of each flower and a separate array to hold the names of each section. We’ll index into each based on the current section/row being displayed, so no more switch statements!

To begin, edit RootViewController.h to read as seen in Listing 1.

Listing 1.
#import <UIKit/UIKit.h>

@class DetailViewController;

@interface RootViewController : UITableViewController {
DetailViewController *detailViewController;
NSMutableArray *flowerData;
NSMutableArray *flowerSections;
}

-(void) createFlowerData;

@end

We’ve added two NSMutableArrays: flowerData and flowerSection. These will hold our flower and section information, respectively. We’ve also declared a method createFlowerData, which will be used to add the data to the arrays.

Next, open the RootViewController.m implementation file and add the createFlowerData method shown in Listing 2.

Listing 2.
 1: - (void)createFlowerData {
2:
3: NSMutableArray *redFlowers;
4: NSMutableArray *blueFlowers;
5:
6: flowerSections=[[NSMutableArray alloc] initWithObjects:
7: @"Red Flowers",@"Blue Flowers",nil];
8:
9: redFlowers=[[NSMutableArray alloc] init];
10: blueFlowers=[[NSMutableArray alloc] init];
11:
12: [redFlowers addObject:[[NSMutableDictionary alloc]
13: initWithObjectsAndKeys:@"Poppy",@"name",
14: @"poppy.png",@"picture",
15: @"http://en.wikipedia.org/wiki/Poppy",@"url",nil]];
16: [redFlowers addObject:[[NSMutableDictionary alloc]
17: initWithObjectsAndKeys:@"Tulip",@"name",
18: @"tulip.png",@"picture",
19: @"http://en.wikipedia.org/wiki/Tulip",@"url",nil]];
20:
21: [blueFlowers addObject:[[NSMutableDictionary alloc]
22: initWithObjectsAndKeys:@"Hyacinth",@"name",
23: @"hyacinth.png",@"picture",
24: @"http://en.wikipedia.org/wiki/Hyacinth_(flower)",
25: @"url",nil]];
26: [blueFlowers addObject:[[NSMutableDictionary alloc]
27: initWithObjectsAndKeys:@"Hydrangea",@"name",
28: @"hydrangea.png",@"picture",
29: @"http://en.wikipedia.org/wiki/Hydrangea",
30: @"url",nil]];
31:
32: flowerData=[[NSMutableArray alloc] initWithObjects:
33: redFlowers,blueFlowers,nil];
34:
35: [redFlowers release];
36: [blueFlowers release];
37: }


Don’t worry if you don’t understand what you’re seeing; an explanation is definitely in order! The createFlowerData method creates two arrays: flowerData and flowerSections.

The flowerSections array is allocated and initialized in lines 6–7. The section names are added to the array so that their indexes can be referenced by section number. For example, Red Flowers is added first, so it is accessed by index (and section number!) 0, Blue Flowers is added second and will be accessed through index 1. When we want to get the label for a section, we can just reference it as [flowerSections objectAtIndex:section].

The flowerData structure is a bit more complicated. As with the flowerSections array, we want to be able to access information by section. We also want to be able to store multiple flowers per section and multiple pieces of data per flower. So how can we get this done?

First, let’s concentrate on the individual flower data within each section. Lines 3–4 define two NSMutableArrays: redFlowers and blueFlowers. These need to be populated with each flower. Lines 12–30 do just that; the code allocates and initializes an NSMutableDictionary with key/value pairs for the flower’s name (name), image file (picture), and Wikipedia reference (url) and inserts it into each of the two arrays.

Wait a second, doesn’t this leave us with two arrays when we wanted to consolidate all of the data into one? Yes, but we’re not done. Lines 32–33 create the final flowerData NSMutableArray using the redFlowers and blueFlowers arrays. What this means for our application is that we can reference the red flower array as [flowerData objectAtIndex:0] and [flowerData objectAtIndex:1] (corresponding, as we wanted, to the appropriate table sections).

Finally, lines 35–36 release the temporary redFlowers and blueFlowers arrays. The end result will be a structure in memory that resembles Figure 3.

Figure 3. The data structure that will populate our table view.


Red Flowers

NamePictureURL
Gerberagerbera.pnghttp://en.wikipedia.org/wiki/Gerbera
Peonypeony.pnghttp://en.wikipedia.org/wiki/Peony
Roserose.pnghttp://en.wikipedia.org/wiki/Rose
Hollyhockhollyhock.pnghttp://en.wikipedia.org/wiki/Hollyhock
Straw Flowerstrawflower.pnghttp://en.wikipedia.org/wiki/Strawflower

Blue Flowers

NamePictureURL
Sea Hollyseaholly.pnghttp://en.wikipedia.org/wiki/Sea_holly
Grape Hyacinthgrapehyacinth.pnghttp://en.wikipedia.org/wiki/Grape_hyacinth
Phloxphlox.pnghttp://en.wikipedia.org/wiki/Phlox
Pin Cushion Flowerpincushionflower.pnghttp://en.wikipedia.org/wiki/Scabious
Irisiris.pnghttp://en.wikipedia.org/wiki/Iris_(plant)


Populating the Data Structures

The createFlowerData method is now ready for use. We can call it from within the RootViewController’s viewDidLoad method. Because an instance of the RootViewController class is calling one of its own methods, it is invoked as [self createFlowerData]:

- (void)viewDidLoad {
[self createFlowerData];
[super viewDidLoad];
}

Remember, we need to release the flowerData and flowerSections when we’re done with them. Be sure to add the appropriate releases to the dealloc method:

- (void)dealloc {
[flowerData release];
[flowerSections release];
[super dealloc];
}

Adding the Image Resources

As you probably noticed when entering the data structures, the application references images that will be placed alongside the flower names in the table. In the project files provided online, find the Flowers folder, select all the images, and drag them into your Xcode resources folder for the project. If you want to use your own graphics, size them at 100×75 pixels (and 200×150 for @2x iPhone 4 images), and make sure the names of the images stored with the picture NSMutableDictionary key match what you add to your project.

Creating the Detail View

The next task in developing the application is building the detail view and view controller. This view has a very simple purpose: It displays a URL in an instance of a UIWebView. We automatically gain the ability to navigate back to the previous view through the project’s navigation controller, so we can focus solely on designing this view.

Creating a New View Controller

Begin by creating a new view controller called FlowerDetailViewController using the UIViewController subclass, as follows:

1.
In Xcode, choose File, New File, Cocoa Touch Class, and then the UIViewController subclass.

2.
Be sure to click the With XIB for user interface check box.

3.
Click Next.

4.
Make sure that Also Create FlowerDetailViewController.m is selected.

5.
Click Finish.

The implementation, header, and associated XIB for the new view controller will be added to the project.

Adding Outlets and Properties

The objects that we’ll need to manipulate within the new detail view are very simple. There will need to be an outlet for accessing the UIWebView instance that we’ll add to the XIB in a bit, as well as a place for storing and accessing the URL that the web view will display. We’ll call these detailWebView and detailURL, respectively. Edit the FlowerDetailViewController header to read as shown in Listing 3.

Listing 3.
#import <UIKit/UIKit.h>

@interface FlowerDetailViewController : UIViewController {
IBOutlet UIWebView *detailWebView;
NSURL *detailURL;
}

@property (nonatomic, retain) NSURL *detailURL;
@property (nonatomic, retain) UIWebView *detailWebView;

@end

Remember to clean up by releasing the detailWebView and detailURL objects in the FlowerDetailViewController.m implementation file’s dealloc method:

- (void)dealloc {
[detailWebView release];
[detailURL release];
[super dealloc];
}

With this work out of the way, we can now implement the logic of the FlowerDetailViewController itself.

Implementing the Detail View Controller Logic
When the view is loaded, the UIWebView instance (detailWebView) should be instructed to load the web address stored within the NSURL object (detailURL).

We only have an NSURL (detailURL), we also need to use the NSURLRequest class method requestWithURL to return the appropriate object type. A single line of code takes care of all of this:
[detailWebView loadRequest:[NSURLRequest requestWithURL:detailURL]]

Add this to the viewDidLoad method in FlowerDetailViewController.m:

- (void)viewDidLoad {
[detailWebView loadRequest:[NSURLRequest requestWithURL:detailURL]];
[super viewDidLoad];
}

Adding the Web View in Interface Builder

Open the FlowerDetailViewController.xib in Interface Builder. You should see a single view in the Document window. We could replace this view entirely with a web view, but by implementing the web view as a subview, we leave ourselves a canvas for expanding the view’s interface elements in the future.

Add a web view by opening the library (Tools, Library) and dragging a web view (UIWebView) onto the existing View icon. It should now appear as a subview within the view (see Figure 4).

Figure 4. Add a web view object as a subview of the existing view.



Connect the web view to the detailWebView outlet by Control-dragging from the File’s Owner icon to the Web View icon in the Document window. When prompted, choose the detailWebView outlet, as demonstrated in Figure 5.

Figure 5. Connect the file’s owner to the detailWebView outlet.


Congratulations, the detail view is now finished! All that remains is providing data to the root view table controller and invoking the detail view through the navigation controller.


Implementing the Root View Table Controller

In the project template we’re working with, Apple has provided a table view controller called RootViewController for us to build from.

Even though we’re using a navigation controller, very little changes between how we implemented our initial table view controller and how we will be building this one. Once again, we need to satisfy the appropriate data source and delegate protocols to provide an interface to our data. We also need to react to a row touch to drill down to our detail view.

The biggest change to the implementation will be how we access our data. Because we’ve built a somewhat complex structure of arrays of dictionaries, we need to make absolutely sure we’re referencing the data that we intend to.

Creating the Table View Data Source Methods

Instead of completely rehashing the implementation details, let’s just review how we can return the needed information to the various methods.

As with the previous example, start by implementing the data source methods within RootViewController.m. Remember, these methods (numberOfSectionsInTableView, tableView:numberOfRowsInSection, and tableView:titleforHeaderInSection) must return the number of sections, the rows within each section, and the titles for the sections, respectively.

To return the number of sections, we just need the count of the elements in the flowerSections array:

[flowerSections count]

Retrieving the number of rows within a given section is only slightly more difficult. Because the flowerData array contains an array for each section, we must first access the appropriate array for the section, and then return its count:

[[flowerData objectAtIndex:section] count]

Finally, to provide the label for a given section via the tableView:titleforHeaderInSection method, the application should index into the flowerSections array by the section value and return the string at that location:

[flowerSections objectAtIndex:section]

Edit the appropriate methods in RootViewController.m so that they return these values.

Populating the Cells with Text and Images

The final mind-bending hurdle that we need to deal with is how to provide actual content to the table cells. As before, this is handled through the tableView:cellForRowAtIndexPath, but unlike the previous example, we need to dig down into our data structures to retrieve the correct results.

Recall that we will be setting the cell’s label using an approach like this:

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

In addition to the label, however, we also need to set an image that will be displayed alongside the label in the cell. Doing this is very similar to setting the label:

[[cell imageView] setImage:[UIImage imageNamed:@"MyPicture.png"]]

To use our own labels and images, however, things get a bit more complicated. Let’s quickly review the three-level hierarchy of our flowerData structure:

flowerData(NSMutableArray)? → NSMutableArray → NSMutableDictionary

The first level, the top flowerData array, corresponds to the sections within the table. The second level, another array contained within the flowerData array, corresponds to the rows within the section, and, finally, the NSMutableDictionary provides the individual pieces of information about each row. Refer to Figure 13.12 if you’re still having trouble picturing how information is organized.

So, how do we get to the individual pieces of data that are three layers deep? By first using the section value to return the right array, and then, from that, using the row value to return the right dictionary, and then finally, using a key to return the correct value from the dictionary.

For example, to get the value that corresponds to the "name" key for a given section and row, we can write the following:

[[[flowerData objectAtIndex:indexPath.section] objectAtIndex: indexPath.row] objectForKey:@"name"]

Likewise, we can return the image file with this:
[[[flowerData objectAtIndex:indexPath.section] objectAtIndex: indexPath.row] objectForKey:@"picture"]

Substituting these values into the statements needed to set the cell label and image, we get the following:
[[cell textLabel] setText:[[[flowerData objectAtIndex:indexPath.section] objectAtIndex: indexPath.row] objectForKey:@"name"]]

and
[[cell imageView] setImage:[UIImage imageNamed:[[[flowerData objectAtIndex:indexPath.section] objectAtIndex: indexPath.row] objectForKey:@"picture"]]]


Add these lines to the tableView:cellForRowAtIndexPath method before the statement that returns the cell.

As a final decoration, the cell can display an arrow on the right side to show that it can be touched to drill down to a detail view. This UI element is called a “disclosure indicator” and can be added simply by setting the accessoryType property for the cell object:

cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator

Add this line after your code to set the cell text and image. The table display setup is now complete.

Handling Navigation Events

Our implementation will need to create an instance of the FlowerDetailViewController and set its detailURL property to the URL that we want the view to display. Finally, the new view controller must be pushed onto the navigation controller stack.

Putting all these pieces together, the result is shown in Listing 4.

Listing 4.
 1: - (void)tableView:(UITableView *)tableView
2: didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
3:
4: FlowerDetailViewController *flowerDetailViewController =
5: [[FlowerDetailViewController alloc] initWithNibName:
6: @"FlowerDetailViewController" bundle:nil];
7: flowerDetailViewController.detailURL=
8: [[NSURL alloc] initWithString:
9: [[[flowerData objectAtIndex:indexPath.section] objectAtIndex:
10: indexPath.row] objectForKey:@"url"]];
11: flowerDetailViewController.title=
12: [[[flowerData objectAtIndex:indexPath.section] objectAtIndex:
13: indexPath.row] objectForKey:@"name"];
14: [self.navigationController pushViewController:
15: flowerDetailViewController animated:YES];
16: [flowerDetailViewController release];
17: }


By the Way

The navigationController instance that we’re using in this code was created by the application template and is defined in the MainWindow.xib and application delegate files. You don’t need to write any code at all to initialize or allocate it.


Lines 4–6 allocate an instance of the FlowerDetailViewController and load the FlowerDetailViewController.xib file. Lines 7–8 set the detailURL property of the new detail view controller to the value of the dictionary key for the selected cell’s section and row.

The detail view controller instance, flowerDetailViewController, is now prepped and ready to be displayed. In lines 11–13, it is pushed on the navigation controller stack. Setting the animated parameter to "YES" implements a smooth sliding action onscreen.


Tweaking the Table UI

Before we can call this application “done,” we need to make a few tweaks to the interface. First, if you’ve run the app already, you know that the table view row just isn’t large enough to accommodate the images that were provided. Second, we need to set a title to be displayed in the navigation bar for the initial table view. This title will then be used to create the label in the “back” button on the subsequent detail view.

Changing the Row Size

To update the height of the rows in the table, open the XIB file that defines the table view (RootViewController.xib) in Interface Builder. Open the Document window and make sure that the Table View icon is selected. Press Command+3 to open the Size Inspector window. Update the row height size to at least 100 points, as shown in Figure 6.

Figure 6. Update the row height to fit the size of the images that will be displayed.


Setting the Table Style

So far, both tables we’ve created use the “plain” style. To change to the more rounded “grouped” style, within the RootViewController.xib, select the Table View icon, and open the Attributes Inspector. Use the Style drop-down menu to switch between the Plain and Grouped options.

By the Way

If you set sizing information for one style of table, and then change the style, your previous size selections will be lost.


Setting a Navigation Bar Title

The title that appears in the navigation bar usually comes from a few different places. If a UINavigationItem object exists in a view controller, the title property of that object will appear as the label in the center of the navigation bar. If no UINavigationItem exists within the view controller, the controller’s title property is used as the navigation bar’s center label.

In this example application, the MainWindow.xib contains an instance of the table view controller (RootViewController) and a UINavigationItem. To set a title that will appear in the navigation bar when the table view is present (and also in the back button of the detail view), open the MainWindow.xib and select the Navigation Item from the Document window. With the item selected, press Command+1 to open the Attributes Inspector. Enter an appropriate title into the Title field, such as Flower List.

Make sure you save all of your files because you are finished! Build and run the FlowerInfoNavigator application—try tapping through a few flowers. With a reasonably minor amount of coding, we’ve created what feels like a very complex iPhone application!

Other  
  •  Windows Phone 7 Development : Push Notifications - Introducing the Push Notifications Architecture
  •  Windows Phone 7 Development : Push Notifications - Understanding Push Notifications
  •  Windows Phone 7 Development : Handling Multiple Concurrent Requests with Rx.NET
  •  WAP and Mobile HTML Security : Application Attacks on Mobile HTML Sites
  •  WAP and Mobile HTML Security : Authentication on WAP/Mobile HTML Sites & Encryption
  •  iPhone Application Development : Displaying and Navigating Data Using Table Views - Building a Simple Table View Application
  •  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
  •  
    Most View
    Dell C3765DNF - Scores High On Print Speed And Quality
    Apple Mac Pro Desktop Computer
    Buyer’s Guide: Touchscreen Monitors for Windows 8 (Part 1) : liyama T2250MTS, Dell ST2220T
    Wired2Fire Diablo Ultima - An Absolutely Appropriate PC For You
    Dell XPS 12 - A Swiveling Good Start
    Portable Drive Algato Thunderbolt SSD
    Run Retro Games On Phone, Tablet And PC (Part 2)
    Downloading and Installing 3CX Phone System (part 1)
    Run Software In A Protective Sandbox
    SQL Injection : Platform-Level Defenses - Using Runtime Protection (part 1) - Web Application Firewalls
    Top 10
    The Long Road To Success - Aston Martin Vanquish - Rapidely Gaining Interest - Aston Martin Rapide S (Part 3)
    Extra-Curricular Activity - BMW 218d Active Tourer SE - An Engine That’s Fit For A Juke - Nissan Juke Tekna DIG-T (Part 3)
    The Long Road To Success - Aston Martin Vanquish - Rapidely Gaining Interest - Aston Martin Rapide S (Part 2)
    The Long Road To Success - Aston Martin Vanquish - Rapidely Gaining Interest - Aston Martin Rapide S (Part 1)
    Extra-Curricular Activity - BMW 218d Active Tourer SE - An Engine That’s Fit For A Juke - Nissan Juke Tekna DIG-T (Part 2)
    Extra-Curricular Activity - BMW 218d Active Tourer SE - An Engine That’s Fit For A Juke - Nissan Juke Tekna DIG-T (Part 1) - BMW 218d Active Tourer SE
    Review : Garmin Fenix 2
    Middle-earth: Shadow Of Mordor
    Review : Leica V-Lux (Typ 114)
    Review : Apple iPad Air 2