View Controllers: How to switch between views programmatically?

80,818

Solution 1

You can begin from the simplest removeFromSuperview/insertSubview and add code to it little by little.


//SwitchViewController.h
#import 
@class BlueViewController;
@class YellowViewController;

@interface SwitchViewController : UIViewController {
    IBOutlet BlueViewController *blueViewController;
    IBOutlet YellowViewController *yellowViewController;
}
- (IBAction)switchViews:(id)sender;
@property (nonatomic, retain) BlueViewController *blueViewController;
@property (nonatomic, retain) YellowViewController *yellowViewController;
@end

//1. remove yellow view and insert blue view
- (IBAction)switchViews:(id)sender {
    if(self.blueViewController.view.superview == nil)
    {
        [yellowViewController.view removeFromSuperview];
        [self.view insertSubview:blueViewController.view atIndex:0];
    }
}

//2. appear=insert, disappear=remove
if(blueViewController.view.superview == nil)
{
    [blueViewController viewWillAppear:YES];
    [yellowViewController viewWillDisappear:YES];

    [yellowViewController.view removeFromSuperview];
    [self.view insertSubview:self.blueViewController.view atIndex:0];

    [yellowViewController viewDidDisappear:YES];
    [blueViewController viewDidAppear:YES];
}

//3. now add animation
[UIView beginAnimations:@"View Flip" context:nil];
[UIView setAnimationDuration:1.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
//blue view will appear by flipping from right
if(blueViewController.view.superview == nil)
{
    [UIView setAnimationTransition: UIViewAnimationTransitionFlipFromRight 
                            forView:self.view cache:YES];

    [blueViewController viewWillAppear:YES];
    [yellowViewController viewWillDisappear:YES];

    [yellowViewController.view removeFromSuperview];
    [self.view insertSubview:self.blueViewController.view atIndex:0];

    [yellowViewController viewDidDisappear:YES];
    [blueViewController viewDidAppear:YES];
}
[UIView commitAnimations];

Solution 2

There's a nice example of switching views in Chapter 6 of Beginning iPhone Development. You can see the source code for it here: http://iphonedevbook.com/

SwitchViewController has the code to change views programatically.


- (IBAction)switchViews:(id)sender
{

    if (self.yellowViewController == nil)
    {
        YellowViewController *yellowController = [[YellowViewController alloc]
                initWithNibName:@"YellowView" bundle:nil];
        self.yellowViewController = yellowController;
        [yellowController release];
    }

    [UIView beginAnimations:@"View Flip" context:nil];
    [UIView setAnimationDuration:1.25];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

    UIViewController *coming = nil;
    UIViewController *going = nil;
    UIViewAnimationTransition transition;

    if (self.blueViewController.view.superview == nil) 
    {   
        coming = blueViewController;
        going = yellowViewController;
        transition = UIViewAnimationTransitionFlipFromLeft;
    }
    else
    {
        coming = yellowViewController;
        going = blueViewController;
        transition = UIViewAnimationTransitionFlipFromRight;
    }

    [UIView setAnimationTransition: transition forView:self.view cache:YES];
    [coming viewWillAppear:YES];
    [going viewWillDisappear:YES];
    [going.view removeFromSuperview];
    [self.view insertSubview: coming.view atIndex:0];
    [going viewDidDisappear:YES];
    [coming viewDidAppear:YES];

    [UIView commitAnimations];

}

Solution 3

If I understand correctly, what you are trying to accomplish is pretty straightforward.

Just add a UINavigationController on your application delegate and do:

[navigationController pushView:vcA];

Delegates will be called accordingly:

  • (void)viewWillAppear:(BOOL)animated;
  • (void)viewDidDisappear:(BOOL)animated;
  • (void)viewDidLoad;

And when you want to pop the view and push another one:

[navigationController popViewControllerAnimated:true];
[navigationController pushView:vcB];

If you don't want the navigationController showing just use:

[navigationBar setHidden:YES];

Where navigationBar is the UINavigationBar corresponding to your UINavigationController.

Solution 4

This may be an old issue, but I recently came across the same problem and had a hard time finding something that worked. I wanted to switch between two complementary view controllers, but I wanted the switch to be animated (built in animations work fine), and I wanted it to be compatible with storyboards if possible.

For taking advantage of built-in transition, UIView's +transitionFromView:toView:duration:options:completion: method works beautifully. But, it only transitions between views, not view controllers.

For the transition to be between whole view controllers, not just views, creating a custom UIStoryboardSegue is the way to go. Whether or not you use storyboards, this approach lets you encapsulate the whole transition and manage the passing of relevant information from one view controller to the next. It only involves subclassing UIStoryboardSegue and overriding a single method, -perform.

For a reference implementation, see RAFlipReplaceSegue, the exact custom segue I put together using this approach. As a bonus, it also replaces the old view controller with the new if it is in a UINavigationController stack.

Share:
80,818
Thanks
Author by

Thanks

I like to play guitar. Sometimes I need to develop software. But I hate it ;) I mean... it sucks. It really does. Well, not always. Oh, and I think I'm the guy with the most questions here.

Updated on December 15, 2020

Comments

  • Thanks
    Thanks over 3 years

    In short: I want to have two fullscreen views, where I can switch between view A and view B. I know I could just use an Tab Bar Controller, but I dont want to. I want to see how this is done by hand, for learning what's going on under the hood.

    I have an UIViewController that acts as an root controller:

    @interface MyRootController : UIViewController {
        IBOutlet UIView *contentView;
    }
    @property(nonatomic, retain) UIView *contentView;
    
    @end
    

    The contentView is hooked up to an UIView which I added as an subview to the "view" of the Nib. This has green color and I see it fullscreen. Works fine.

    Then, I created two other View Controllers pretty much the same way. ViewControllerA and ViewControllerB. ViewControllerA has a blue background, ViewControllerB has a black background. Just to see which one is active.

    So, in the implementation of myRootController, I do this:

    // Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        ViewControllerA *vcA = [[ViewControllerA alloc] initWithNib];
        [self.contentView addSubview:vcA.view];
    
        [cvA release];
    }
    

    By the way, the -initWithNib method looks like this:

    - (id)initWithNib { // Load the view nib
        if (self = [super initWithNibName:@"ViewA" bundle:nil]) {
            // do ivar initialization here, if needed
        }
        return self;
    }
    

    That works. I see the view from ViewControllerA when I start the app. But now the big question is: A View Controller typically has all those methods like:

    • (void)viewWillAppear:(BOOL)animated;
    • (void)viewDidDisappear:(BOOL)animated;
    • (void)viewDidLoad;

    ...and so on. Who or what, or how would those methods be called if I do it "my" way without a tab bar controller? I mean: If I allocate that ViewController's class and the view get's visible, would I have to take care about calling those methods? How does it know that viewWillAppear, viewDidDisappear, or viewDidLoad? I believe that the Tab Bar Controller has all this "cleverness" under the hood. Or am I wrong?

    UPDATE: I've tested it. If I release the view controller (for example: ViewControllerA), I will get no log message on viewDidDisappear. Only when allocating and initializing the ViewControllerA, I get an viewDidLoad. But that's it. So all signs stand for the cleverness of UITabBarController now ;) and I have to figure out how to replicate that, right?

  • Thanks
    Thanks about 15 years
    sounds good but I dont want any kind of navigation or tab bar ;) I just want naked views with a button "go to B", and on the other "go to A". Or is there a way of not having that navigation bar above?
  • Pablo Santa Cruz
    Pablo Santa Cruz about 15 years
    You can definitely hide the navigation bar if you don't want it showing on the screen. I will update the answer with the info.
  • matm
    matm about 13 years
    +1 for referencing where you get the example (just what @iPhoney forgot to do).