You are on page 1of 90


and Tab Bar Controllers and Table View

Stack of view controllers Naviga)on bar

How It Fits Together

Top view controller s view Top view controller s )tle Previous view controller s )tle Top view controller s toolbar items (iPhone OS 3.0)

The views of a naviga)on interface

Objects managed by the naviga)on controller

The naviga)on stack

Loading Your Naviga&on Interface from a Nib File

Crea)ng a Naviga)on Controller Programma)cally

- (void)applica)onDidFinishLaunching:(UIApplica)on *)applica)on{ UIViewController *rootController = [[MyRootViewController alloc] init]; naviga)onController = [[UINaviga)onController alloc] initWithRootViewController:rootController]; [rootController release]; window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; [window addSubview:naviga)onController.view]; [window makeKeyAndVisible];}

Modifying the Naviga)on Stack

Push to add a view controller - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated; Pop to remove a view controller - (UIViewController *)popViewControllerAnimated:(BOOL)animated; Set to change the en&re stack of view controllers (iPhone OS 3.0) - (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated;

Pushing Your First View Controller

- (void)applica)onDidFinishLaunching { // Create a naviga)on controller navController = [[UINaviga)onController alloc] init]; // Push the rst view controller on the stack [navController pushViewController:rstViewController animated:NO]; // Add the naviga)on controller s view to the window [window addSubview:navController.view]; }

In Response to User Ac)ons

Push from within a view controller on the stack - (void)someAc)on:(id)sender { // Poten)ally create another view controller UIViewController *viewController = ...; [self.naviga)onController pushViewController:viewController animated:YES]; } Almost never call pop directly!
Automa)cally invoked by the back bu_on

Messages sent during stack changes

A Controller for Each Screen

Connec)ng View Controllers

Mul)ple view controllers may need to share data One may need to know about what another is doing
Watch for added, removed or edited data Other interes)ng events

How Not To Share Data

Global variables or singletons
This includes your applica&on delegate!

Direct dependencies make your code less reusable

And more dicult to debug & test

Best Prac)ces for Data Flow

Figure out exactly what needs to be communicated Dene input parameters for your view controller For communica)ng back up the hierarchy, use loose coupling
Dene a generic interface for observers (like delega)on)

Customizing Naviga)on
Bu_ons or custom controls Interact with the en)re screen

Naviga)on bar styles

The objects associated with a naviga)on bar

Describes appearance of the naviga)on bar
Title string or custom )tle view Lee & right bar bu_ons More proper)es dened in UINaviga)onBar.h

Every view controller has a naviga&on item for customizing

Displayed when view controller is on top of the stack

Naviga)on Item Ownership

The objects associated with a naviga)on bar

Displaying a Title
UIViewController already has a )tle property
@property(nonatomic,copy) NSString *)tle;

Naviga)on item inherits automa)cally

Previous view controller s )tle is displayed in back bu_on

viewController.)tle = @ Detail ;

Lee & Right Bu_ons

Special object, denes appearance & behavior for items in naviga)on bars and toolbars

Display a string, image or predened system item Target + ac)on (like a regular bu_on)

Text Bar Bu_on Item

- (void)viewDidLoad { UIBarBu_onItem *fooBu_on = [[UIBarBu_onItem alloc] initWithTitle:@"Foo style:UIBarBu_onItemStyleBordered target:self ac)on:@selector(foo:)]; self.naviga)onItem.leeBarBu_onItem = fooBu_on; [fooBu_on release]; }

System Bar Bu_on Item

- (void)viewDidLoad { UIBarBu_onItem *addBu_on = [[UIBarBu_onItem alloc] initWithBarBu_onSystemItem:UIBarBu_onSystemItemAdd style:UIBarBu_onItemStyleBordered target:self ac)on:@selector(add:)]; self.naviga)onItem.rightBarBu_onItem = addBu_on; [addBu_on release]; }

Edit/Done Bu_on
Very common pa_ern Every view controller has one available
Target/ac)on already set up

self.naviga)onItem.leeBarBu_onItem = self.editBu_onItem; // Called when the user toggles the edit/done bu_on - (void)setEdi)ng:(BOOL)edi)ng animated:(BOOL)animated { // Update appearance of views }

Custom Title View

Arbitrary view in place of the )tle

UISegmentedControl *segmentedControl = ... self.naviga)onItem.)tleView = segmentedControl; [segmentedControl release];

Back Bu_on
Some)mes a shorter back bu_on is needed

self.)tle = @ Hello there, CS193P! ; UIBarBu_onItem *heyBu_on = [[UIBarBu_onItem alloc] initWithTitle:@ Hey! ...]; self.naviga)onItem.backBu_onItem = heyBu_on; [heyBu_on release];

// View 3 - Custom right bar bu_on with a view UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems: [NSArray arrayWithObjects: [UIImage imageNamed:@"up.png"], [UIImage imageNamed:@"down.png"], nil]]; [segmentedControl addTarget:self ac)on:@selector(segmentAc)on:) forControlEvents:UIControlEventValueChanged]; segmentedControl.frame = CGRectMake(0, 0, 90, kCustomBu_onHeight); segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;segmentedControl.momentary = YES; defaultTintColor = [segmentedControl.)ntColor retain]; // keep track of this for later UIBarBu_onItem *segmentBarItem = [[UIBarBu_onItem alloc] initWithCustomView:segmentedControl]; [segmentedControl release]; self.naviga)onItem.rightBarBu_onItem = segmentBarItem; [segmentBarItem release];

Toolbar items in a naviga)on interface

(void)congureToolbarItems {

UIBarBu_onItem *exibleSpaceBu_onItem = [[UIBarBu_onItem alloc] initWithBarBu_onSystemItem:UIBarBu_onSystem ItemFlexibleSpace target:nil ac)on:nil]; // Create and congure the segmented control UISegmentedControl *sortToggle = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:@"Ascending, @"Descending", nil]]; sortToggle.segmentedControlStyle = UISegmentedControlStyleBar; sortToggle.selectedSegmentIndex = 0; [sortToggle addTarget:self ac)on:@selector (toggleSor)ng:) forControlEvents:UIControlEventValueChanged];

// Create the bar button item for the segmented control UIBarButtonItem *sortToggleButtonItem = [[UIBarButtonItem alloc] initWithCustomView:sortToggle]; [sortToggle release]; // Set our toolbar items self.toolbarItems = [NSArray arrayWithObjects: flexibleSpaceButtonItem, sortToggleButtonItem, flexibleSpaceButtonItem, nil]; [sortToggleButtonItem release]; [flexibleSpaceButtonItem release]; }

Array of view controllers Tab bar

How It Fits Together

Selected view controller s view All view controllers )tles

The views of a tab bar interface

A tab bar controller and its associated view controllers

Tab bar items of the iPod applica)on

Nib le containing a tab bar interface

Seong Up a Tab Bar Controller

- (void)applica)onDidFinishLaunching // Create a tab bar controller tabBarController = [[UITabBarController alloc] init]; // Set the array of view controllers tabBarController.viewControllers = myViewControllers; // Add the tab bar controllers view to the window [window addSubview:tabBarController.view]; }

Tab Bar Appearance

View controllers can dene their appearance in the tab bar

Title + image or system item

Each view controller comes with a tab bar item for customizing

Crea)ng Tab Bar Items

Title and image - (void)viewDidLoad { UITabBarItem *item = [[UITabBarItem alloc] initWithTitle:@Playlists image:[UIImage imageNamed:@music.png] tag:0]; self.tabBarItem = item; [item release]; }

Crea)ng Tab Bar Items

System item - (void)viewDidLoad { UITabBarItem *item = [[UITabBarItem alloc] initWithTabBarSystemItem: UITabBarSystemItemBookmarks tag:0] self.tabBarItem = item; [item release]; }

Crea)ng the view controllers tab bar item

- (id)init { if (self = [super initWithNibName:@"MyViewController" bundle:nil]) { self.)tle = @"My View Controller; UIImage* anImage = [UIImage imageNamed:@"MyViewControllerImage.png"]; UITabBarItem* theItem = [[UITabBarItem alloc] initWithTitle:@"Home" image:anImage tag:0]; self.tabBarItem = theItem; [theItem release]; } return self; }

Removing the current tab

- (IBAc)on)processUserInforma)on:(id)sender{ // Call some app-specic method to validate the user data. // If the custom method returns YES, remove the tab. if ([self userDataIsValid]) { NSMutableArray* newArray = [NSMutableArray arrayWithArray:self.tabBarController.viewControllers]; [newArray removeObject:self]; [self.tabBarController setViewControllers:newArray animated:YES]; } }

- (BOOL)tabBarController:(UITabBarController *)aTabBar

Preven)ng the selec)on of tabs

shouldSelectViewController:(UIViewController *)viewController

if (![self hasValidLogin] && (viewController != [aTabBar.viewControllers objectAtIndex:0]) ) { // Disable all but the first tab. return NO; } return YES; }

More View Controllers

Tab Bar + Naviga)on Controllers

Mul)ple parallel hierarchies

Tab Bar + Naviga)on Controllers

Nes)ng Naviga)on Controllers

Create a tab bar controller tabBarController = [[UITabBarController alloc] init]; Create each naviga)on controller navController = [[UINaviga)onController alloc] init]; [navController pushViewController:rstViewController animated:NO]; Add them to the tab bar controller tabBarController.viewControllers = [NSArray arrayWithObjects: navController, anotherNavController, someViewController, nil];

Table Views
Display lists of content
Single column, mul)ple rows Ver)cal scrolling Large data sets

Powerful and ubiquitous in iPhone applica)ons

Table View Styles

UITableViewStylePlain UITableViewStyleGrouped

Table View Anatomy

Plain Style
Table Header Section Header Table Cell Section Footer Section

Table Footer

Table View Anatomy

Grouped Style
Table Header Section Header Table Cell Section Footer Section

Table Footer

Header and footer of a sec)on

Declaring methods that allow you to congure the appearance of the table view
specifying the default height of rows or providing a view used as the header for the table access to the currently selected row as well as specic rows or cells manage selec)ons, scroll the table view, and insert or delete rows and sec)ons.

UITableView inherits from UIScrollView, which denes scrolling behavior for views with content larger than the size of the window.
UITableView redenes the scrolling behavior to allow ver)cal scrolling only.

Using Table Views

Displaying your data in the table view Customizing appearance & behavior

Displaying Data in a Table View

A Nave Solution
Table views display a list of data, so use an array [myTableView setList:myListOfStuff]; Issues with this approach
All data is loaded upfront All data stays in memory

A More Flexible Solution

Another object provides data to the table view
Not all at once Just as its needed for display

Like a delegate, but purely data-oriented

Calling sequence for crea)ng and conguring a table view

Provide number of sections and rows // Optional method, defaults to 1 if not implemented - (NSInteger)numberOfSectionsInTableView:(UITableView *)table; // Required method - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; Provide cells for table view as needed // Required method - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

Datasource Message Flow

numberOfSectionsInTableView: tableView:numberOfRowsInSec)on: tableView:cellForRowAtIndexPath:

Generic class in Foundation Path to a specific node in a tree of nested arrays

NSIndexPath and Table Views

Cell location described with an index path
Section index + row index

Category on NSIndexPath with helper methods @interface NSIndexPath (UITableView) + (NSIndexPath *)indexPathForRow:(NSUInteger)row inSection:(NSUInteger)section; @property(nonatomic,readonly) NSUInteger section; @property(nonatomic,readonly) NSUInteger row; @end

Crea&ng a Table View Programma&cally @interface RootViewController : UIViewController <UITableViewDelegate,

UITableViewDataSource> { NSArray *timeZoneNames; } @property (nonatomic, retain) NSArray *timeZoneNames; @end

- (void)loadView {
UITableView *tableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStylePlain]; tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight| UIViewAutoresizingFlexibleWidth; tableView.delegate = self; tableView.dataSource = self; [tableView reloadData]; self.view = tableView; [tableView release]; }

Single Section Table View

Return the number of rows - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [myStrings count]; } Provide a cell when requested - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = ...; cell.textLabel.text = [myStrings objectAtIndex:indexPath.row] return [cell autorelease]; }

Cell Reuse
When asked for a cell, it would be expensive to create a new cell each time.
- (UITableViewCell *)tableView:(UITableView *)tableView *)dequeueReusableCellWithIdentifier: cellForRowAtIndexPath:(NSIndexPath *)indexPath (NSString *)identifier; { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@MyIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:... reuseIdentifier:@MyIdenifier] autorelease]; } cell.text = [myStrings objectAtIndex:indexPath.row] return cell; }

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [regions count]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Number of rows is the number of time zones in the region for the specified section. Region *region = [regions objectAtIndex:section]; return [region.timeZoneWrappers count]; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { // The header for the section is the region name -- get this from the region at the section index. Region *region = [regions objectAtIndex:section]; return [region name]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *MyIdentifier = @"MyIdentifier; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease]; } Region *region = [regions objectAtIndex:indexPath.section]; TimeZoneWrapper *timeZoneWrapper = [region.timeZoneWrappers objectAtIndex:indexPath.row]; cell.textLabel.text = timeZoneWrapper.localeName; return cell; }

Triggering Updates
When is the datasource asked for its data?
When a row becomes visible When an update is explicitly requested by calling reloadData

- (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self.tableView reloadData]; }

Section and Row Reloading

- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; - (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;

Calling sequence for inser)ng or dele)ng rows in a table view

Mapping levels of the data model to table views

Additional Datasource Methods

Titles for section headers and footers Allow editing and reordering cells

Appearance & Behavior

UITableView Delegate
Customize appearance and behavior Keep application logic separate from view Often the same object as datasource

Table View Appearance & Behavior

Customize appearance of table view cell - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; Validate and respond to selection changes - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath; - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;

Row Selection in Table Views

In iPhone applications, rows rarely stay selected Selecting a row usually triggers an event

Persistent Selection

Responding to Selection
// For a navigation hierarchy... - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // Get the row and the object it represents NSUInteger row = indexPath.row id objectToDisplay = [myObjects objectAtIndex:row]; // Create a new view controller and pass it along MyViewController *myViewController = ...; myViewController.object = objectToDisplay; [self.navigationController pushViewController:myViewController animated:YES]; }

Altering or Disabling Selection

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath { // Dont allow selecting certain rows? if (indexPath.row == ...) { return nil; } else { return indexPath; } }


Convenient starting point for view controller with a table view
Table view is automatically created Controller is table views delegate and datasource

Takes care of some default behaviors

Calls -reloadData the first time it appears Deselects rows when user navigates back Flashes scroll indicators

Table View Cells

Designated Initializer
- (id)initWithFrame:(CGRect)frame reuseIden)er:(NSString *)reuseIden)er;



- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier;

Cell Styles
UITableViewCellStyleDefault UITableViewCellStyleSubtitle

UITableViewCellStyleValue1 UITableViewCellStyleValue2

Basic properties
UITableViewCell has an image view and one or two text labels
cell.imageView.image = [UIImage imageNamed:@vitolidol.png]; cell.textLabel.text = @Vitol Idol; cell.detailTextLabel.text = @Billy Idol;

Accessory Types
// UITableView delegate method - (UITableViewCellAccessoryType)tableView:(UITableView *)table accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath;
UITableViewCellAccessoryDisclosureIndicator UITableViewCellAccessoryDetailDisclosureButton UITableViewCellAccessoryCheckmark

- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath { // Only for the blue disclosure button NSUInteger row = indexPath.row; ... }

Customizing the Content View

For cases where a simple image + text cell doesnt suffice UITableViewCell has a content view property
Add additional views to the content view

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = ...; CGRect frame = cell.contentView.bounds; UILabel *myLabel = [[UILabel alloc] initWithFrame:frame]; myLabel.text = ...; [cell.contentView addSubview:myLabel]; [myLabel release]; }