Navigating between views

Most iOS applications consist of more than one view. To manage the flow between views, a UINavigationController is used.

In this example, I'll use a UINavigationController to help switch between the list of gas cans, and the details of a specific can.

To start, open GasCanManAppDelegate.m and alter the method as follows:

  1. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  2. {
  3. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  4. // Override point for customization after application launch.
  5.  
  6. // GasCanDetailViewController *baseView = [[GasCanDetailViewController alloc] init];
  7. // [self.window setRootViewController:baseView];
  8.  
  9. // GasCansViewController *tableView = [[GasCansViewController alloc] init];
  10. // [self.window setRootViewController:tableView];
  11.  
  12.  
  13. GasCansViewController *tableView = [[GasCansViewController alloc] init];
  14. UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:tableView];
  15. [self.window setRootViewController:navController];
  16.  
  17. self.window.backgroundColor = [UIColor whiteColor];
  18. [self.window makeKeyAndVisible];
  19. return YES;
  20. }

Note that both prior examples are commented out. However, since the GasCansViewController will be the root of the navigation, it's instantiated and added to the navigation controller. Running the app at this time will result in exactly the same results as at the end of the prior section. (Well, there's extra space at the top which appears blank, but other than that, it's the same.)

Since there are multiple views, it makes sense to fill in that extra space with a title. Open GasCansViewController.m and add the following line of code to the init method:
[[self navigationItem] setTitle:@"Gas Can Manager"];

The init method should now appear as this:

  1. -(id) init {
  2. self = [super initWithStyle:UITableViewStyleGrouped];
  3. if (self) {
  4. [[self navigationItem] setTitle:@"Gas Can Manager"];
  5.  
  6. [[GasCanStore canStore] createCan:5 withName:@"Can one"];
  7. [[GasCanStore canStore] createCan:4 withName:@"Can two"];
  8. [[GasCanStore canStore] createCan:3 withName:@"Can three"];
  9. [[GasCanStore canStore] createCan:2 withName:@"Can four"];
  10. [[GasCanStore canStore] createCan:1 withName:@"Can five"];
  11. }
  12. return self;
  13. }

Each item has a reference to the 'navigationItem' property. From there, it's possible to set the title of the view.

In a prior section, I hard-coded the details of the gas can. Now it's time to go back and update that class so that it can be given a "real" gas can. Update GasCanDetailsViewController.h as follows:

  1. #import <UIKit/UIKit.h>
  2.  
  3. @class GasCan;
  4.  
  5. @interface GasCanDetailViewController : UIViewController {
  6.  
  7. __weak IBOutlet UITextField *name;
  8. __weak IBOutlet UITextField *level;
  9. __weak IBOutlet UITextField *size;
  10. }
  11.  
  12. @property (nonatomic, strong) GasCan *can;
  13.  
  14. @end

There is a forward declaration of the GasCan class, and a gas can property is defined.

Update GasCanDetailViewController.m as follows:

  1. #import "GasCanDetailViewController.h"
  2. #import "GasCan.h"
  3.  
  4. @implementation GasCanDetailViewController
  5.  
  6. @synthesize can;
  7.  
  8. -(void) setCan:(GasCan *)c {
  9. can = c;
  10. [[self navigationItem] setTitle: can.name];
  11. }
  12.  
  13. - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
  14. {
  15. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  16. if (self) {
  17. // Custom initialization
  18. }
  19. return self;
  20. }
  21.  
  22. -(void)viewWillAppear:(BOOL)animated {
  23. [super viewWillAppear:animated];
  24.  
  25. [name setText:can.name];
  26. NSString *tmp = [NSString stringWithFormat:@"%f", can.level];
  27. [level setText: tmp];
  28. tmp = [NSString stringWithFormat:@"%i",can.size];
  29. [size setText: tmp];
  30.  
  31. }
  32.  
  33. -(void)viewWillDisappear:(BOOL)animated {
  34. [super viewWillDisappear:animated];
  35.  
  36. [[self view] endEditing:YES];
  37.  
  38. float curVal = [[level text] floatValue];
  39. if (curVal <= can.size) {
  40. can.level = curVal;
  41. }
  42. }
  43.  
  44. //- (void)viewDidLoad
  45. //{
  46. // [super viewDidLoad];
  47. // // Do any additional setup after loading the view from its nib.
  48. // [name setText:@"My Gas Can"];
  49. // [level setText:@"2.5"];
  50. // [size setText:@"5"];
  51. //}
  52.  
  53. @end

Some comments:

  • The GasCan.h file is imported
  • The can property is synthesized. However, I'm overriding the setter so that I can update the view's title.
  • viewWillAppear and viewWillDisappear are called when (you guessed it) the view is about to be displayed, or undisplayed. On display, it's necessary to update the text fields. When it will disappear, it's time to update the underlying object with the new level of the gas can. (For now, we'll keep name and size in read-only mode.)
  • Since the view may appear/disappear frequently, there's no need for the viewDidLoad method.

Now that the detailed view is ready to accept a gas can and display / update the details, all that remains is to invoke the view. This is done in the table view when an entry is clicked. Update GasCansViewController.m to include the following method:

  1. -(void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  2. GasCanDetailViewController *details = [[GasCanDetailViewController alloc] init];
  3.  
  4. NSArray *allCans = [[GasCanStore canStore] allCans];
  5. GasCan *curCan = [allCans objectAtIndex:[indexPath row]];
  6. [details setCan:curCan];
  7.  
  8. [[self navigationController] pushViewController:details animated:YES];
  9. }

This code creates an instance of the GasCanDetailViewController, sets the currently selected can on the new object, and then pushes this new view onto the view stack. Now, when the project is run, and an item is selected, the details will be displayed. You will also note that there is now a "back" button on the details view:




At this point, GasCanMan is a fully-functional, multi-view application.