Thursday, April 23, 2009

iPhone tutorial: "Navigation-Based Application"

The "Navigation-Based Application" template in XCode introduces us to the "navigation controller" (UINavigationController) and the "table view" (UITableView), which is a real beast, since you can use it for almost everything! Because of that, it is also very popular as you can see by playing around in the standard iPhone applications. If you are unsure where to look, check out "Settings" (the gears-icon on the home screen), since that is the most obvious user of the table view.

Table view is basically used to display a list of items in a table, where each row consists of a cell, which in its turn consists of a couple of "views" filled with content (text, icons, etc.). The table view also offers a "edit mode" which you can use to, for example, edit the contents of the cells or rearrange the rows.

This template also introduces a lot of other concepts, so this tutorial will probably be broken into many parts.

Enough talk! Start up XCode and choose "File/New Project" from the menu and select the "Navigation-Based Application" template and name the project "Nav1". Expand the Classes and Resources groups in the XCode's "Groups and files" section in the left of XCode's main window that. This shows you that the project has the following structure:

Nav1
Classes
RootViewController.[hm]
Nav1AppDelegate.[hm]
Resources
RootViewController.xib
MainWindow.xib
Info.plist

Click on Info.plist to confirm that "MainWindow.xib" is set as the "Main nib base name". After that click on Nav1AppDelegate.h. If you've read the previous posts, you'll see that it starts out in a standard manner; it implements the UIApplicationDelegate protocol and declares a 'window' property (UIWindow). After that a UINavigationController called 'nagivationController' is declared, which we haven't seen before. Both these are defined as properties and marked as IBOutlet so that we can manipulate them from Interface Builder.

Classes/Nav1AppDelegate.m

- (void) applicationDidFinishLaunching:(UIApplication *)application {
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
}

When we implement the 'applicationDidFinishLaunching' method from the UIApplicationDelegate interface we are given a chance to "do stuff" right after the application has been loaded, so that everything will look good when it finally appears on the screen. Here we decide that the view of our UINavigationController should be displayed at startup and therefore we add it as a subview to our window by calling addSubView. We also make our window visible and ensure that it is the "first responder" (of events) by calling makeKeyAndVisible.

Ok, now that we have some understanding of what application does at startup, let's take it for a test run by building and running (CMD-Return). Not too exciting, huh? A white screen with a blue bar at the top. But wait, if you look carefully, you'll also notice that there are some thin grey horizontal lines evenly spaced down the white background. Yep, that's a table view with empty cells! The thin lines you see are the row separators.

Resources/MainWindow.xib

Double-click on the file in XCode to start Interface Builder (IB). As always when working with non-trivial IB-files, it's best to switch view mode to the hierarchical mode (middle button above the text "View Mode" in the upper left corner of the MainWindow.xib window in IB). After doing this, you'll see that the IB-file has the following structure:

File's Owner [UIApplication]
First Responder [UIResponder]
Nav1 App Delegate [Nav1AppDelegate]
Window [UIWindow]
Navigation Controller [UINavigationController]
Navigation Bar [UINavigationBar]
Root View Controller [RootViewController]
Navigation Item [UINavigationItem]

File's Owner [UIApplication]

CMD-4 shows nothing special. CMD-2 shows us that the 'delegate' property is connected to an object called "Nav1 App Delegate", which is ours.

First Responder [UIResponder]

Nothing special.

Nav1 App Delegate [Nav1AppDelegate]

CMD-4 shows the class as Nav1AppDelegate which we already knew since we're using the hierarchical view mode in the MainWindow.xib window. Our properties 'window' and 'navigationController' are also shown here. CMD-2 shows us that 'window' is connected to an object called "Window" and 'navigationController' is connected to an object called "Navigation Controller". Both these are explained below. We also see that the "File's owner".delegate property is set to point to this object.

Window [UIWindow]

CMD-4 shows it's a plain old UIWindow. CMD-2 shows that the "Nav1 App Delegate".window property is set to point to this object.

Navigation Controller [UINavigationController]

CMD-4 shows it's a UINavigationController that has one outlet called 'delegate'. Without knowing the details of how a navigation controller works, we can guess that 'delegate' should point to an object which wants to receive messages from (having it's methods invoked from) this object. CMD-2 shows that this object has four outlets in total, but on CMD-4 we only saw one ('delegate'). This suggests that UINavigationController is a subclass and if you're memory is good you probably recognise the 'navigationItem', 'tabBarItem' and 'view' outlets from an UIViewController.

Let's verify this by going back to XCode, select Nav1AppDelegate.h, double-click on UINavigationController to select that word in the source code. Position the mouse over the selected word and CTRL-click and select "Find Selected Text in API Reference". This should bring up a window containing the documenation for the UINavigationController class. There you can see that it inherits from UIViewController : UIResponder : NSObject. Good.

CMD-2 also shows that the "Nav1 App Delegate".navigationController property is connected to this object.

Navigation Bar [UINavigationBar]

CMD-4 shows that this is a UINavigationBar with a 'delegate' outlet. CMD-2 shows that none of its outlets are connected.

Root View Controller [RootViewController]

CMD-4 shows the class is RootViewController, which is one of our classes, and has no outlets (of its own). CMD-2 however shows the standard UIViewController outlets so this class apparently inherits from that class. The 'navigationItem' outlet is connected to an object called "Navigation Item". If we search for 'navigationItem' in the XCode API docs window which we popped up a while ago and click on the search result which represents the property of that name, we read that the navigation item referred by the property is used to [visually] represent the view controller when it is "pushed onto a navigation bar". Is it some kind of icon or what? Read on and you'll find out.

CMD-1 normally doesn't show anything exciting, but here it does. It shows that this object will be loaded from an IB-file called "RootViewController", which exists in the Resources group of our XCode project. If you've read the previous posts, you are familiar with the concept of a "placeholder" (proxy) object like this one.

Navigation Item [UINavigationItem]

CMD-4 shows the class as UINavigationItem and a rather long list of outlets; 'backBarButtonItem', 'leftBarButtonItem', 'rightBarButtonItem' and 'titleView'. CMD-2 shows that none of these properties are connected to anything. Maybe that's why we saw no buttons and no title when we test ran the application from XCode in the iPhone simulator? Sounds reasonable, but to really understand what these outlets are for I suggest you search for 'UINavigationItem' in the API docs window in XCode.

From the API doc we can read that a navigation bar (the blue bar at the top on the screen you saw when you test ran the application) normally displays a "Back"-button to the left and a title in the center. The "Back"-button is normally labelled with title of the "back" (previous) screen, but you can override this by setting the 'backBarButtonItem' property. The docs also indicates that if you accept the standard behaviour, you won't need to set any of the properties. Good, that means that we can leave them for now...

Or wait, not just yet! If you press CMD-1, you will see that there are some edit boxes for "Title" "Prompt" and "Back Button" which lets you set the text for those properties. This means, that if you are satisfied with just having a custom text - compared with having custom buttons - you can enter the text here. Try entering "MyTitle", "MyPrompt" and "MyBack" in the boxes and then take a look at the MainWindow.xib window. See that a small arrow appeared to the left of "Navigation Item"? Press it to expand the hierarchy and you'll see that a "Bar Button Item (MyBack)" object was added. If you're really observant you'll also see that "(MyTitle)" was added on the row of the "Navigation Item". Here IB offers a really simple way of customising the navigation controller - it even creates the necessary "Bar Button Item" object for you.

The "MyPrompt" text we entered isn't visible in the MainWindow.xib window, but if you double-click on the "Navigation Item (MyTitle)" row in the window, the "edit window" will pop up and you'll be able to see the navigation controller we are designing. There, at the top of the screen, you'll see the "MyPrompt" text as well. That text is actually assigned to the 'prompt' property. After seeing this you should delete the text though, because setting it seems to trigger a bug in XCode which makes it impossible to build the application.

Bar Button Item (MyBack) [UIBarButtonItem]

CMD-4 shows that the class is UIBarButtonItem. CMD-2 shows that there are no outlets, but it also shows a section called "Sent Actions" which we aren't used to seeing here. We might return to that later.

Ok, save the changes in IB (CMD-S) and go back to XCode and build and run (CMD-R). You did remember to clear the "Prompt" field of the "Navigation Item" right? Otherwise it won't build. Now the application should look a bit more exciting since it displays "MyTitle" in the blue navigation bar at the top of the screen.

Resources/RootViewController.xib

This is quite small IB file which has the following structure.

File's Owner
First Responder
Table View

Remember that this file is intended to be "loaded into" one of the objects in MainWindow.xib? This was configured by setting the "NIB name" attribute (CMD-1) of "Root View Controller" to "RootViewController". This means that "Root View Controller" will "own" this file, which brings us to...

File's Owner [RootViewController]

CMD-4 shows that the class of this object is our class RootViewController - that is an object of that class will "own" this file. It also shows that it has no outlets of its own, but if we take a brief look at RootViewController.h in XCode we can see that it is a subclass of UITableViewController, which in turn is a subclass of UIViewController. This means that at least the standard view controller outlets are available.

CMD-2 indeed shows that there are quite a lot of outlets; 'navigationItem', 'tabBarItem', 'tableView' and 'view'. We have seen three of these before, but 'tableView' is new and comes from UITableViewController. An interesting thing here is that both 'tableView' and 'view' are connected to the same object - "Table View". This makes sense since "Table View" is of the class UITableView which inherits from UIView, so 'view' is connected to the "UIView part" of "Table View".

CMD-2 also shows that there are two referencing outlets, that is, properties of other objects that are connected to this object; "Table View".dataSource and "Table View".delegate to be precise.

First Responder [UIResponder]

As always(!) quite uninteresting...

Table View [UITableView]

CMD-4 shows it is of the class UITableView and has a 'dataSource' outlet. A search for UITableView in the XCode API docs window brings up the docs for that class and after expanding the Properties section in the left frame we can click on 'dataSource' and read that it should be set to an object which implements the UITableViewDataSource protocol. Now that we know that we can move on.

CMD-2 shows two outlets; 'dataSource' and 'delegate', but where did 'delegate' come from since we didn't see it under the Identity tab (CMD-4)? Yet another look in the XCode API docs window for UITableView shows that it is a subclass of UIScrollView and that's where the 'delegate' property comes from. It also shows that the protocol implemented by 'delegate' should be UIScrollViewDelegate.

Both these properties are connected to "File's owner" which is the RootViewController in MainWindow.xib. This means that that object will receive messages from both the UITableView and the UIScrollView part.

Finally, we see that there are two referencing outlets. Once again that means that some other object's outlets have been connected to this object. Here it is "File's Owner".tableView and "File's Owner".view that has been connected to this object.

Time for a break

Phew! I initially told you that this template would introduce a lot of new concepts and it sure did! All this text and so far we have only analysed the IB files and a little of the source code and still we only have an empty table! At least I need a break now, so the rest of the analysis and some more interesting stuff will follow in part two.


1 comment:

  1. This is not a good method to represent the any thing if you post the with pic. then more better and all are use it as compare to this.
    but nice effort improve the technique.

    ReplyDelete