96 Comments

Summary:

In this tutorial, you will learn how to do the following: Create and run a Navigation-Based Application from XCode Create and add a user interface, designed in Interface Builder, as a sub-view to a navigation based application Navigate to sub-views from a UITableView Allow sub-views to […]

In this tutorial, you will learn how to do the following:

  • Create and run a Navigation-Based Application from XCode
  • Create and add a user interface, designed in Interface Builder, as a sub-view to a navigation based application
  • Navigate to sub-views from a UITableView
  • Allow sub-views to access application data

Creating and Running a Navigation-Based Application in XCode

Let’s start off by opening up XCode and creating a Navigation-Based Application.

001

Click Choose… and give your application the name BasicNavigation. Once completed, your project window should look like this.

002

At this point, if you click “Build and Go” and have your project selected to run on the iPhone simulator, you should see the application launch and display a bare UITableView, with undefined row elements. No worries, this is going to change very soon. Your app at this point should look like the following below.

003

Adding a Sub-view (Designed in Interface Builder) to a UITableView

We are now going to add a sub-view to our application. This sub-view will consist of three key elements.

  • A Header File (*.h file extension) — Used for defining methods and variables to be used by an accompanied implementation file.
  • An Implementation File (*.m file extension) — Used for implementing methods and accessing variables defined in an accompanied header file. This is where we will place code for view specific logic and functionality.
  • An Interface File (*.xib extension) — Used for defining the visual look of the view, using Interface Builder.

In XCode, within the project area on the left, right-click on the Classes folder and select Add, then New File…. You will be presented with the following dialogue.

004

Make sure you’ve selected Cocoa Touch Classes on the left and select the file template for UIViewController subclass as illustrated above, and click Next. You will be presented with a dialogue in which you will assign a name for this asset. Name this file SubViewOneController.m and make sure the checkbox for Also create ‘SubViewOneController.h’ is checked as well. Click Finish when this is done. The dialogue should look like the following, before clicking on Finish.

005

At this point, you will see two additional files in the Classes folder that we’ve just added. So far we’ve added a header file (SubViewOneController.h) and an implementation file (SubViewOneController.m), which takes care of two out of three key elements. We’ll now add the last element here before moving on towards implementation.

In XCode, right-click the Resources folder and select Add, then New File…. On the left, select User Interfaces and select View XIB and click next. In the next dialogue, you will give this user interface a name. Type SubViewOne.xib under File Nam” and click Finish. These steps are illustrated below.

006

007

The contents of your project window should now look similar to the illustration below.

008

Open the file SubViewOneController.h and add the following code:

#import <UIKit/UIKit.h>

@interface SubViewOneController : UIViewController {
	IBOutlet UILabel *label;
	IBOutlet UIButton *button;
}

@property (retain,nonatomic) IBOutlet UILabel *label;
@property (retain,nonatomic) IBOutlet UIButton *button;

- (IBAction) OnButtonClick:(id) sender;

@end

Open the file “SubViewOneController.m” and add the following code:

#import "SubViewOneController.h"

@implementation SubViewOneController

@synthesize label, button;

- (id) init {
	self = [super init];
	if (self != nil) {
		// set the title of this view
		self.title = NSLocalizedString(@"Subview One", @"");
	}
	return self;
}

- (IBAction) OnButtonClick:(id) sender {
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}

- (void)dealloc {
    [super dealloc];
}

@end

What we’ve done thus far is write a bit of view controller code. In our code, we’ve declared a variable for a UILabel and a UIButton. We’ve also created a stubbed handler method for responding on the button click action. What we haven’t done yet is create a user interface to go with our view controller code. Let’s go ahead do that now.

In the project window, let’s expand the Resources node, and double click on the file SubViewOne.xib. You will then see Interface Builder open this file and be presented with a blank View file. In the menu bar, click on Tools, then Library. When the Library window opens, click on the Inputs & Values node. The Library window will look this.

009

Drag a UILabel and a UIButton such that your view mimics the following interface.

010

At this point, if you have not saved and built your project, please do so by going back into XCode and clicking the Build button. If all is well, everything will build successfully. We’re not done just yet, so let’s head back to Interface Builder.

Highlight File’s Owner and then from the menu bar, click on Tools, then Identity Inspector. In the Class text input, type or scroll down to the menu option that says SubViewOneController. Your view properties should now look similar to the following.

011

In the next steps we are going to wire up the user interface elements of the view we’ve created to the view controller we created earlier. First make sure File’s Owner is selected. In the menu bar, click on Tools, then Connections Inspector.

Under Outlets and Received Actions in the Connections Inspector make the following associations:

  • Drag the “button” outlet to the button on the visual interface
  • Drag the “label” outlet to the label on the visual interface
  • Drag the “view” outlet to anywhere on the visual interface
  • Drag the “onButtonClick” action to the button on the visual interface. Select “Touch Up Inside”

012

At this point we’ve created our first sub-view, however we’re not going to be able to get to it from application just yet. That’s the next step.

Before moving on, go back into XCode and click on Build before moving onto the next part. Your build should compile and the results should report back successfully at this point.

Navigating to a Sub-View from a UITableView

The code below will do the following:

  • Sets up an array of views and supplies that array to our UITable as a data source
  • Adds multiple sub views to our application using the sub view we’ve designed and written thus far
  • Allows us to navigate to different sub views upon view selection from our UITableView

Open the file “RootViewController.h” and add the following code:

#import <UIKit/UIKit.h>

@interface RootViewController : UITableViewController {
	IBOutlet NSMutableArray *views;
}

@property (nonatomic, retain) IBOutlet NSMutableArray *views;

@end

Open the file “RootViewController.m” and add the following code:

#import "RootViewController.h"
#import "SubViewOneController.h"

@implementation RootViewController

@synthesize views;

- (void)awakeFromNib {
	// we'll keep track of our views in this array
	views = [ [NSMutableArray alloc] init];

	// allocate a set of views and add to our view array as a dictionary item
	SubViewOneController *subViewOneController = [[SubViewOneController alloc] init];
	subViewOneController.title = @"Subview One";
	[views addObject:[NSDictionary dictionaryWithObjectsAndKeys:
						@"Subview One",			@"title",
						subViewOneController,	@"controller",
						nil]];
	[subViewOneController release];

	subViewOneController = [[SubViewOneController alloc] init];
	subViewOneController.title = @"Subview Two";
	[views addObject:[NSDictionary dictionaryWithObjectsAndKeys:
					  @"Subview Two",			@"title",
					  subViewOneController,	@"controller",
					  nil]];
	[subViewOneController release];

	subViewOneController = [[SubViewOneController alloc] init];
	subViewOneController.title = @"Subview Three";
	[views addObject:[NSDictionary dictionaryWithObjectsAndKeys:
					  @"Subview Three",			@"title",
					  subViewOneController,	@"controller",
					  nil]];
	[subViewOneController release];

	// create a custom navigation bar button and set it to always say "Back"
	UIBarButtonItem *temporaryBarButtonItem = [[UIBarButtonItem alloc] init];
	temporaryBarButtonItem.title = @"Back";
	self.navigationItem.backBarButtonItem = temporaryBarButtonItem;
	[temporaryBarButtonItem release];

	//set the title of the main view
	self.title = @"Basic Navigation";
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}

#pragma mark Table view methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [views count];
}

// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
    }

    // Set up the cell...
	cell.text = [[views objectAtIndex:indexPath.row] objectForKey:@"title"];

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // Navigation logic may go here. Create and push another view controller.
	UIViewController *targetViewController = [[views objectAtIndex: indexPath.row] objectForKey:@"controller"];
	[[self navigationController] pushViewController:targetViewController animated:YES];
}

- (void)dealloc {
	[views dealloc];
    [super dealloc];
}

@end

Click the “=Build and Go button in XCode. Our application thus far should now allow us to select a sub view and navigate to it. The application should look similar to the following:

013

014

Accessing Global Application Data

In an iPhone application, the Application Delegate can commonly be used to store global variables accessible by all views. You may run into a situation where a view may require data that has been modified by another view or may need to be notified of updates to this data. This piece of the tutorial will cover that very scenario. In this, we’ll be covering some key points in our code:

  • Subscribing to central events
  • Broadcasting events globally
  • Reading/writing global application data

To begin, let’s open the file BasicNavigationAppDelegate.h and add the following code.

#import <UIKit/UIKit.h>

@interface BasicNavigationAppDelegate : NSObject <UIApplicationDelegate> {

	//Application Model Data
	NSString *modelData;

    UIWindow *window;
    UINavigationController *navigationController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;

- (void) setModelData:(NSString *)modelData;
- (NSString *) getModelData;

@end

Now open BasicNavigationAppDelegate.m and add the following code.

#import "BasicNavigationAppDelegate.h"
#import "RootViewController.h"

@implementation BasicNavigationAppDelegate

@synthesize window;
@synthesize navigationController;

- (void)applicationDidFinishLaunching:(UIApplication *)application {
	// Configure and show the window
	[window addSubview:[navigationController view]];
	[window makeKeyAndVisible];
}

- (void)applicationWillTerminate:(UIApplication *)application {
	// Save data if appropriate
}

// accessor methods for "data" property

- (void) setModelData:(NSString *) newData {
	modelData = newData;
	[[NSNotificationCenter defaultCenter] postNotificationName:@"dataChangeEvent" object:self];
}

- (NSString *) getModelData {
	if ( modelData == nil ) {
		modelData = @"Hello World";
	}
	return modelData;
}

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

@end

We’ve added a model variable and accessor methods to read and write to this variable. Notice on the setModelData method, how we have the following line [[NSNotificationCenter defaultCenter] postNotificationName:@"dataChangeEvent" object:self]. When this setter method is called, we will be broadcasting an event, dataChangeEvent, to observers of this event notification. For this application, our sub views will subscribe to this event notification.

Go back and open up SubViewController.h and add the following line:

- (void) getModelData;

Now open SubViewOneController.m, and update the code to look like the following:

#import "SubViewOneController.h"
#import "BasicNavigationAppDelegate.h"

@implementation SubViewOneController

@synthesize label, button;

- (id) init {
	self = [super init];
	if (self != nil)
	{
		// subscribe to changes on global data and course of action to take
		[[NSNotificationCenter defaultCenter]
		 addObserver:self
		 selector:@selector(onDataChangeEvent:)
		 name:@"dataChangeEvent"
		 object:nil];
	}
	return self;

}

- (IBAction) OnButtonClick:(id) sender {
	// this view will set our model data
	BasicNavigationAppDelegate *appDelegate =
	(BasicNavigationAppDelegate *)[[UIApplication sharedApplication] delegate];
	NSString *displayString = [NSString stringWithFormat: @"Set By %@", [self title]];
	[appDelegate setModelData:displayString];
}

- (void)onDataChangeEvent:(id) sender {
	[self getModelData];
}

-(void)viewWillAppear:(BOOL)animated {
	[self getModelData];
}

// set our label to reflect the latest copy of the data that we're observing
- (void) getModelData {
	BasicNavigationAppDelegate *appDelegate =
	(BasicNavigationAppDelegate *)[[UIApplication sharedApplication] delegate];
	[label setText:[appDelegate getModelData] ];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}

- (void)dealloc {
    [super dealloc];
}

@end

Let’s note the relevant things that are going on here:

  • The view subscribes to the dataChangeEvent on the init method. When the dataChangeEvent is fired, the method onDataChangeEvent is triggered and executed.
  • On Button click, we will be setting the the model data located in our application delegate. Remember, that in our implementation, whenever that variable is set, the OnDataChangeEvent is fired and all observers of that event will respond to it

Here’s how we know that our sub views are operating off the same global data.

  • Click on Sub View One, then click the button in that view. You’ll notice the label will change to “Set By Subview One”
  • Click “back”, then click on Sub View Two. Notice the label says “Set By Subview One”
  • Click the button, the label should change in Sub View Two to “Set By Subview Two”
  • Click “back, then click on Sub View Three. Notice the label says “Set By Subview Two”

Do you notice what’s happening here? Our data is persisting across views and this data can be read and set by any of our sub views. What we’ve demonstrated here are some very basic practices in model-view-control design and implementation. I hope this lesson carries well for you. Happy coding.

  1. When I enter in a SubView, I can’t see anything.

    There is no button, no label. :(

    Share
  2. Yup – same here. — must be something missing.. I am looking at the apple dev sample for UICatalog to see if I can see what went wrong.

    Share
  3. [...] Create a Navigation-Based Application: This tutorial will teach you how to create and run a navigation-based application from XCode. [...]

    Share
  4. please tell us how add one more view to this we need six frames to view ,please guide us any one its an urgent requirement

    Share
  5. Adam Boulanger Thursday, April 23, 2009

    The solution to the SubViewOneController’s view not appearing is that you need to tell the controller that the view will be initialized from a nib file. Go into SubViewOneController.m and un-comment the code starting with :
    – (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {

    change:
    [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]
    to:
    [super initWithNibName:@"SubViewOne" bundle:nil]

    then it should load the associated view object from the nib file of that name.

    Share
    1. If u change:

      [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]

      to:

      return [super initWithNibName:@"SubViewOne" bundle:nil];

      it will work fine.

      Share
  6. James Revolti Friday, April 24, 2009

    Great tutorial! Congratulations!
    It works fine here!

    But if I click twice in the button, the program crashes when I select a new view.

    Share
  7. I’m encountering trouble at the “Navigating to a Sub-View from a UITableView” part. I just added that huge bunch of code to rootviewcontroller.m , and now it doesn’t work.I get 3 warnings, each at the “SubViewOneController.title = @”Subview Two”;” areas for subview one, subview two, and subview three.
    The warnings all say “warning: ‘subviewoneController’ may not respond to ‘-alloc’.”
    When I click build and go, it seems to build ok but as soon as the sim phone loads my program it crashes.

    I would love some help.

    Share
  8. never mind about that comment. I was able to fix it myself.

    Share
    1. So what did you do Julian?

      Share
  9. [...] now, when I tap the button, it says "set by [whichever category it is in]" because the tutorial (noob, I know) said I should put a code to make it access global application data. I’m still not [...]

    Share
  10. Julian again Sunday, April 26, 2009

    Ok I have a real question. I did everything you said to do in this tutorial. I am just wondering how to make certain text appear when I click the button. (as you can probably see im a real noob)
    Since it says “set by…”, I figured that means there is some way to make it say what you want. I have most of the code i need ready. Im trying to make it so when you tap the button it picks a random number and displays the text associated with that number. How do I make that text display on the label in the subview?

    Share

Comments have been disabled for this post