Chapter 4
View Controllers

Modified

Downloads

iPhone SDK Development examples - http://pragprog.com/titles/amiphd

Resources

See the text, on page 73, for detailed instructions on the ModelEditViewController, presented later.

Overview

The text presents a ViewController, MovieViewController, to display three UILabel and a UIButton.

No interaction is implemented, only display of the UILabel content when the the application view is displayed.

MovieViewController

Key points:

  1. inherits from UIViewController,
     
  2. -viewDidLoad delegate used to initialize view controller objects, here a new Movie object,
     
  3. -viewWillAppear delegate used to refresh display, necessary when switching between views.
     
  4. @property(nonatomic, retain)  UILabel *titleLabel; defines instance variable getter/setter semantics; not thread-safe, release old object, retain new object in setter.
     
  5. self.titleLabel = nil; in dealloc, the setter releases old object and sets new object to nil.
     
  6. self.titleLabel.text = self.movie.title; getter/setter could also be written as:

        [titleLabel setText: [movie title]];
     
  7. IBOutlet objects have corresponding UILabel defined in the IB for MovieViewController.xib
Movie
#import <Foundation/Foundation.h>

@interface Movie : NSObject {
  NSString *title;
  NSNumber *boxOfficeGross;
  NSString *summary;
}

- (id)initWithTitle:(NSString *)newTitle 
     boxOfficeGross:(NSNumber *)newBoxOfficeGross
            summary:(NSString *)newSummary;

@property(nonatomic, copy) NSString *title;
@property(nonatomic, copy) NSNumber *boxOfficeGross;
@property(nonatomic, copy) NSString *summary;

@end
#import "Movie.h"
@implementation Movie

@synthesize title;
@synthesize boxOfficeGross;
@synthesize summary;

- (id)initWithTitle:(NSString *)newTitle 
     boxOfficeGross:(NSNumber *)newBoxOfficeGross
     summary:(NSString *)newSummary {
  self = [super init]; 
  if(nil != self) {
    self.title = newTitle;
    self.boxOfficeGross = newBoxOfficeGross;
    self.summary = newSummary;
  }
  return self;
}

- (void) dealloc { 			// Setter's release
  self.title = nil;
  self.boxOfficeGross = nil;
  self.summary = nil;
  [super dealloc];
}
@end

MovieViewController

#import <UIKit/UIKit.h>
@class Movie;
@interface MovieViewController : 
                 		UIViewController {
  Movie *movie;
  UILabel *titleLabel;
  UILabel *boxOfficeGrossLabel;
  UILabel *summaryLabel;
}

@property(nonatomic, retain) 
	Movie *movie;
@property(nonatomic, retain) 
	IBOutlet UILabel *titleLabel;
@property(nonatomic, retain) 
	IBOutlet UILabel *boxOfficeGrossLabel;
@property(nonatomic, retain) 
	IBOutlet UILabel *summaryLabel;
- (IBAction)edit;
@end

#import "MovieViewController.h"
#import "Movie.h"

@implementation MovieViewController
@synthesize titleLabel;
@synthesize boxOfficeGrossLabel;
@synthesize summaryLabel;
@synthesize movie;
- (IBAction)edit {
  NSLog(@"edit method invoked");
}
- (void)viewDidLoad {
  [super viewDidLoad];

  Movie *newMovie = [[Movie alloc] 
                     initWithTitle:@"Iron Man" 
                     boxOfficeGross:
		        [NSNumber numberWithFloat:650000000.00] 
                     summary:@"Smart guy makes cool armor"];

  self.movie = newMovie;
  [newMovie release];
}
- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];

  self.titleLabel.text = self.movie.title;

  NSNumberFormatter *formatter = 
                 [[NSNumberFormatter alloc] init];
  [formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
  self.boxOfficeGrossLabel.text = 
  [formatter stringFromNumber:self.movie.boxOfficeGross];
  [formatter release];
  self.summaryLabel.text = self.movie.summary;
}
- (void)dealloc {
  self.titleLabel = nil;
  self.boxOfficeGrossLabel = nil;
  self.summaryLabel = nil;
  self.movie = nil;
  [super dealloc];
}
@end

Refactoring MovieViewController to Add Editing in a Subview

The MovieViewController will be be refactored for the Edit button to open another view controller, MovieEditViewController.

When MovieEditViewController is exited, the MovieViewController is displayed with any changes.

MovieViewController

MovieEditorViewController

A Movie object is accessed in common by the MovieViewController and MovieEditorViewController objects via an alias held in instance variable.

@interface MovieViewController : UIViewController {
  Movie *movie;
  UILabel *titleLabel;
  UILabel *boxOfficeGrossLabel;
  UILabel *summaryLabel;
  MovieEditorViewController *editingViewController;
 }

@interface MovieEditorViewController :
UIViewController  {
  UITextField *titleField;
  UITextField *boxOfficeGrossField;
  UITextField *summaryField;
  Movie *movie;
}

Key points in MovieViewController:

  1. IBOutlet MovieEditorViewController *editingViewController added instance variable to MovieEditorViewController created in IB.
     

  2. -edit method connected to Edit button.
     

  3. -edit method calls MovieEditorViewController setter, setting Movie instance variable.
     

  4. -edit method opens the MovieEditorViewController view by:

    presentModalViewController

     

  5. -viewWillAppear displays Movie object edited in MovieEditorViewController.
     

MovieViewController

#import "MovieViewController.h"
#import "MovieEditorViewController.h"
#import "Movie.h"

@implementation MovieViewController
@synthesize titleLabel;
@synthesize boxOfficeGrossLabel;
@synthesize summaryLabel;
@synthesize movie;
@synthesize editingViewController;
- (IBAction)edit {

    self.editingViewController.movie = self.movie;	// Alias Movie object

    [self presentModalViewController:self.editingViewController 
           animated:YES];
}
- (void)viewDidLoad {
    [super viewDidLoad];
    Movie *newMovie = [[Movie alloc] 
                     initWithTitle:@"Iron Man" 
                     boxOfficeGross:[NSNumber 
                     numberWithFloat:650000000.00] 
                     summary:@"Smart guy makes cool armor"];
    self.movie = newMovie;
    [newMovie release];
}
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    self.titleLabel.text = self.movie.title;

    NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
    [formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
    self.boxOfficeGrossLabel.text = 
    [formatter stringFromNumber:self.movie.boxOfficeGross];
    [formatter release];
    self.summaryLabel.text = self.movie.summary;
}

- (void)dealloc {
    self.titleLabel = nil;
    self.boxOfficeGrossLabel = nil;
    self.summaryLabel = nil;
    self.movie = nil;
    [super dealloc];
}
@end
#import <UIKit/UIKit.h>
@class Movie;
@class MovieEditorViewController;

@interface MovieViewController : UIViewController {
  Movie *movie;
  UILabel *titleLabel;
  UILabel *boxOfficeGrossLabel;
  UILabel *summaryLabel;
  MovieEditorViewController *editingViewController;
 }

@property(nonatomic, retain) 
	Movie *movie;
@property(nonatomic, retain) 
	IBOutlet UILabel *titleLabel;
@property(nonatomic, retain) 
	IBOutlet UILabel *boxOfficeGrossLabel;
@property(nonatomic, retain) 
	IBOutlet UILabel *summaryLabel;
@property(nonatomic, retain) 
IBOutlet MovieEditorViewController *editingViewController;
- (IBAction)edit;

@end

 



		

MovieEditViewController

Key points in MovieEditViewController:

  1. -viewWillAppear method displays values of Movie object set by MovieViewController.
     

  2. MovieEditViewController view closed and MovieViewController displayed by:

    - (IBAction)done {
      [[self parentViewController] 
    	dismissModalViewControllerAnimated:YES];
    }
  3. -textFieldDidEndEditing formats the edited text.
MovieEditViewController
#import "MovieEditorViewController.h"
#import "Movie.h"

@implementation MovieEditorViewController

@synthesize titleField;
@synthesize boxOfficeGrossField;
@synthesize summaryField;
@synthesize movie;

- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
  self.titleField.text = self.movie.title;
  self.summaryField.text = self.movie.summary;
  NSNumberFormatter *formatter = 
			[[NSNumberFormatter alloc] init];
  [formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
  self.boxOfficeGrossField.text = 
            [formatter stringFromNumber:self.movie.boxOfficeGross];
  [formatter release];
}
- (IBAction)done {
  [[self parentViewController] 
	dismissModalViewControllerAnimated:YES];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
  [textField resignFirstResponder];
  return YES;
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
  if(textField == self.titleField) {
    self.movie.title = self.titleField.text;
  } else if(textField == self.boxOfficeGrossField) {
    NSNumberFormatter *formatter = 
			[[NSNumberFormatter alloc] init];
    [formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
    self.movie.boxOfficeGross = 
        [formatter numberFromString:self.boxOfficeGrossField.text];
    [formatter release];
  } else if(textField == self.summaryField) {
    self.movie.summary = self.summaryField.text;
  }
}
- (void)dealloc {
    [super dealloc];
}
@end
#import <UIKit/UIKit.h>
@class Movie;

@interface MovieEditorViewController : 
				UIViewController  {
  UITextField *titleField;
  UITextField *boxOfficeGrossField;
  UITextField *summaryField;
  Movie *movie;
}

@property(nonatomic, retain) 
	IBOutlet UITextField *titleField;
@property(nonatomic, retain) 
	IBOutlet UITextField *boxOfficeGrossField;
@property(nonatomic, retain) 
	IBOutlet UITextField *summaryField;
@property(nonatomic, retain) 
	Movie *movie;

- (IBAction)done;
@end