iOS View Controller Programming

iOS View Controller: UIViewController

To access the view from a View Controller, access the view property

@property UIView *view;

UIViewController Life Cycle Delegate

Initialize View Controller class

- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)aBundle;
  • If nib name is nil, it will try to use the class name as the Nib file name
  • If nib file is not found, build and set your own view in loadView
    - (void)loadView {
      ...
      self.view = ...;
    }
    

iOS calla viewDidLoad when the view is loaded

  • Implement setup code
  • View is likely loaded only once after the application launch
    - (void)viewDidLoad;
    

iOS calls viewWillAppear when view is about to appear

  • A view may appear and disappear many times
  • View's bound/frame data is set before viewWillAppear
    - (void)viewWillAppear:(BOOL)animated;
    

iOS calls viewWillDisappear when view will disappear

- (void)viewWillDisappear:(BOOL)animated
{
  [superviewWillDisappear:animated];
  // Saving data and a view's state
  ...
}

Delegate after view appear and disappear

- (void)viewDidAppear:(BOOL)animated;
- (void)viewDidDisappear:(BOOL)animated;

Called when memory is low

  • Set all outlet to nil
  • Release un-needed memory for the view
    - (void)viewDidUnload;
    

Response to a device rotation

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)anOrientation
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)anOrientation
                               duration:(NSTimeInterval)seconds;
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)anOrientation;

Possible location to initialize data

- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle;
- (void)awakeFromNib
- (void)viewDidLoad
- (void)viewWillAppear:(BOOL)animated;
  • awakeFromNib is called when the view controller is loaded by a .xib file
  • viewDidLoad is often used for initialization code if the view's boundary information is not needed

iOS Navigation Controller: UINavigationController

UINavigationController implements a specialized view controller that manages the navigation of hierarchical stack content (stack content - FILO)

Create a new empty application in Xcode

Initialize the iOS Navigation Controller in the application delegate

  • Initialize UINavigationController
  • Initialize the first view controller to be pushed onto the navigation controller
    MyAppDelegate.m
    - (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
        UINavigationController *nav = [[UINavigationController alloc] init];
        MyViewController *vc = [[MyViewController alloc] init];
        [nav pushViewController:vc animated:NO];
        [window addSubview:nav.view];
        [window makeKeyAndVisible];
    
        return YES;
    }
    

Current view on the top of the Navigation Controller

  • The view property (UIView) of the UIViewController returns the current view of the Navigation Controller
  • The toolbarItems property (NSArray of UIBarButtonItems) returns the top tool bar items at the bottom of the screen

Set the displayed title on the top of the screen

MyViewController.m
- (void)viewDidLoad
{
  self.title = @"My first view";
}

To push another View Controller to the top of the Navigation Controller (Say after pushing a button in MyView)

  • Implement an action to handle the button event on MyViewController
  • Create the second view controller
  • Set the title of the second view controller to be displayed
  • Push and display the second view controller
    • The view controller has a built-in navigationController property pointing to its Navigation Controller
      MyViewController.m
      - (void)navigateToNextVC
      {
      	OtherViewController *vc = [[OtherViewController alloc] init];
      	vc.title = @"My second view";
      	[self.navigationController pushViewController:vc animated:YES];
      }
      

      iOS will use the title of the last view controller as a navigation button (back button) to navigate back to the last view

To navigate back to the last view programmatically

[self.navigationController popViewControllerAnimated:YES];

iOS Tab Bar Controller: UITabBarController

UITabBarController displays tabs at the bottom of the screen for selecting between different views. It acts as the container of other view controller

Initializing UITabBarController

  • Create multiple view controllers switchable by clicking at the bottom navigation tag
    - (BOOL)application:(UIApplication *)
               didFinishLaunchingWithOptions:(NSDictionary *)
    {
       UIViewController *vc1 = ...;
       UIViewController *vc2 = ...;
    
       UITabBarController *container = [[UITabBarController alloc] init];
       container.viewControllers = [NSArray arrayWithObjects: vc1, vc2, ..., nil];
    
       [window addSubview:container.view];
       [window makeKeyAndVisible];
    
       return YES;
    }
    
  • The title of each view is used as the text in the Tab icon

Customized the Icon for each Tab

Call setup from initWithNibName:bundle: or awakeFromNib: to create the custom title and icon in the tab bar

??- (void)setup  {
   UITabBarItem *item = [[UITabBarItem alloc] initWithTitle:@"Calculator"
                          image:[UIImage imageNamed:@"calc.png"]
                            tag:0];
?   self.tabBarItem = item;
}
  • Cannot called from viewDidLoad because the tab bar is already rendered

Using the built-in system icon for the navigation tag

UITabBarItem *item = [[UITabBarItem alloc]
                        initWithSystemItem:UITabBarSystemItemSearch
                                       tag:0];

Setting Badge Value in a Tab Navigation Icon

For example, setting the number of un-read item along with the tab icon

- (void) showBadgeValue
{
   self.tabBarItem.badgeValue = @"3";
}

iOS Popup Controller: UIPopoverController

Create a popup in iOS (UIPopoverController)

Initialize the popup

...
UIPopoverController *popover = [[UIPopoverController] alloc] initWithContentViewController:myViewController];

Popup the view

[popover presentPopoverFromRect:(CGRect)aRect
                         inView:(UIView *)aView
       permittedArrowDirections:(UIPopoverArrowDirection)direction
                       animated:(BOOL)animated];
  • direction control the direction of the arrow in the popup
    UIPopoverArrowDirectionUp
    UIPopoverArrowDirectionDown
    UIPopoverArrowDirectionLeft
    UIPopoverArrowDirectionRight
    UIPopoverArrowDirectionAny
    

Popup from a bar button

[popover presentPopoverFromBarButtonItem:(UIBarButtonItem *)barButtonItem
                permittedArrowDirections:(UIPopoverArrowDirection)direction
                                animated:(BOOL)animated];

To dismiss the popup

[popover dismissPopoverAnimated:(BOOL)animated];

To listen to the popup dismiss event

- (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popover;
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popover;

To control the size of the popup, implement

- (CGSize)contentSizeForViewInPopover;

Check whether the popup is already visible

popover.isPopoverVisible

iOS Split view controller: UISplitViewController

  • For iPad which has bigger screen area to display 2 views side by side
  • Side by side views of 2 view controllers (right and left views) in landscape mode
  • Show the left view but turn the right view to a popover when it is in portrait mode
    • Hit the button on the top navigation bar to popup the right view

Create a split view in iOS

- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)options
{
    UIViewController *vc1 = [[FirstViewController alloc] init];
    UIViewController *vc2 = [[SecondViewController alloc] init];

    UINavigationController *nvc1 = [[UINavigationController alloc] init];
    UINavigationController *nvc2 = [[UINavigationController alloc] init];
    [nvc1 pushViewController:vc1 animated:NO];
    [nvc2 pushViewController:vc2 animated:NO];

    UISplitViewController *split = [[UISplitViewControlleralloc]init];
    split.delegate = vc2;
    split.viewControllers = [NSArray arrayWithObjects:nvc1, nvc2, nil];

    [window addSubview:split.view];
    [window makeKeyAndVisible];
    return YES;
}

We use a Navigation Controller to wrap around the view such that we can use the navigation bar to pop up the hidden view when it is in portrait mode

Handling Portrait and Landscape mode (SplitViewControllerDelegate) for Split View controller

When a split view is switched to a portrait mode, we need to display a navigation button on the top for user to activate the popup

Declare the controller on the right side to be the Split view controller delegate in handling the show/hide left view event

@interface SecondViewController : UIViewController <UISplitViewControllerDelegate>

When rotate to portrait mode, the left view controller will disappear

  • We need to activate a button on the top navigation bar
  • Click on the button will activate a pop up to view the right view
    - (void)splitViewController:(UISplitViewController*)sv
         willHideViewController:(UIViewController *)vc
              withBarButtonItem:(UIBarButtonItem*)barButtonItem
           forPopoverController:(UIPopoverController*)popover
    {
      barButtonItem.title=vc.title;
      self.navigationItem.rightBarButtonItem = barButtonItem;
    }
    

    The use of a Navigation Controller wrapping around a view make us possible to use the navigation bar to create a popup button

Disable the button when switch back to landscape mode

- (void)splitViewController:(UISplitViewController*)sv
   willShowViewController:(UIViewController *)vc
invalidatingBarButtonItem:(UIBarButtonItem*)barButtonItem
{
    self.navigationItem.rightBarButtonItem = nil;
}

Device Rotation with Portrait & Landscape mode

Configure what iOS orientation is supported

CircleViewController.m
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
    } else {
        return YES;
    }
}

Support both iOS portrait and landscape mode

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
	return YES;
}

Example of a View Controller flow

  • Create a navigation view controller
  • Pust the first view controller onto the navigation controller
    MyAppDelegate.m
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
       MyViewController *vc1 = [[MyViewController alloc] init];
       UINavigationController *nav = [[UINavigationController alloc] init];
    
       [nav pushViewController:vc1 animated:NO];
       [window addSubview:nav.view];
    
       [window makeKeyAndVisible];
       return YES;
    }
    

Using loadView to initialize a view

An action respond to a button event push another view controller onto the navigation controller

MyViewController.m
- (void)pushMe
{
  SecondViewController *vc2 = [[SecondViewController alloc] init];
  [self.navigationController pushViewController:vc2 animated:YES];
}

Create a Web View using loadView instead of a Nib file

SecondViewController.m
- (void)loadView
{
  web = [[UIWebView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
  web.scalesPageToFit = YES;
  self.view = web;
}
- (void)viewWillAppear:(BOOL)animated
{
  [super viewWillAppear:animated];
  [web loadRequest:[self someUrlRequest]];
}

Using Nib to initialize a view

MyViewController.m
- (void)pushMe
{
  SecondViewController *vc2 = [[SecondViewController alloc] initWithNibName:@"SecondViewController.xib" bundle:nil];
  [self.navigationController pushViewController:vc2 animated:YES];
}

Animate the spinner

@interface MyViewController : UIViewController
{
  ...
  IBOutlet UIActivityIndicatorView *spinner;
}

To animate the spinner

[spinner startAnimating];
...
Operation that may take time
...
[spinner stopAnimating];