iphoneiosuiviewuiviewcontrolleraugmented-reality

iOS switching view controllers depending on the device orientation


I'm developing an Augmented Reality application, everything worked properly till now that I need two different kind of visualization (AR and Map) depending on the device orientation. In particular the application should use the landscapeViewController when the device is in landscape mode while it should use another controller (named faceUpViewController ) when the device's orientation is "face up". I tried doing it with two simple view controllers and it works fine. The problem happens when the landscapeViewController uses the AR controller. The view is completely white and I don't understand why. Both the two controllers are "contained" by a Root View Controller. I'm doing everything by coding so without nib files. Here is the code:

RootViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
}

- (void)deviceOrientationDidChange:(NSNotification *)notification{

    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];

    if (orientation == UIDeviceOrientationLandscapeLeft) {
        if (self.landscapeViewController.view.superview == nil) {
            if (self.landscapeViewController == nil) {
                LandscapeViewController *lvc = [[LandscapeViewController alloc] init];
                self.landscapeViewController = lvc;
                [lvc release];
            }
            [self.faceUpViewController.view removeFromSuperview];
            [self.view addSubview:self.landscapeViewController.view];
        }
    }

    if (orientation == UIDeviceOrientationFaceUp) {
        if (self.faceUpViewController.view.superview == nil) {
            if (self.faceUpViewController == nil) {
                FaceUpViewController *fvc = [[FaceUpViewController alloc] init];
                self.faceUpViewController = fvc;
                [fvc release];
            }
            [self.landscapeViewController.view removeFromSuperview];
            [self.view addSubview:self.faceUpViewController.view];
        }
    }

}

@end

LandscapeViewController.m

// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
    UIView *landscapeView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
    landscapeView.backgroundColor = [UIColor yellowColor];
    self.view = landscapeView;
    [landscapeView release];

    ARController *arC = [[ARController alloc] initWithViewController:self];
    arC.landscapeViewController = self;
    self.arController = arC;
    [arC release];
}

//When the view appear present the camera feed
- (void)viewDidAppear:(BOOL)animated { 
    [super viewDidAppear:animated]; 
    [_arController presentModalARControllerAnimated:NO];
}

FaceUpViewController.m

- (void)loadView
{
    UIView *faceUpView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1024, 768)];
    faceUpView.backgroundColor = [UIColor blueColor];
    self.view = faceUpView;
    [faceUpView release];
}

ARController.m Very simple version

- (id) initWithViewController:(UIViewController *)theView{

    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {

        self.rootController = theView; 

        //Retrieve screen bounds
        CGRect screenBounds = [[UIScreen mainScreen] bounds]; 

        UIView *overlaidView = [[UIView alloc] initWithFrame: screenBounds];
        self.overlayView =  overlaidView;
        [overlaidView release];
        self.rootController.view = overlayView;

        // Initialise the UIImagePickerController 
        UIImagePickerController *picker= [[UIImagePickerController alloc] init];
        self.pickerController = picker;
        [picker release];

        self.pickerController.sourceType = UIImagePickerControllerSourceTypeCamera; 
        self.pickerController.cameraViewTransform = CGAffineTransformScale(
                                                                           self.pickerController.cameraViewTransform, 1.0f, 1.12412f);

        self.pickerController.showsCameraControls = NO; 
        self.pickerController.navigationBarHidden = YES; 
        self.pickerController.cameraOverlayView = _overlayView;
    }

    return self;
}

- (void)presentModalARControllerAnimated:(BOOL)animated{
    [self.rootController presentModalViewController:[self pickerController] animated:animated]; 
    self.overlayView.frame = self.pickerController.view.bounds;
}

@end

I say again that I'm doing everything by coding thereby without nib files. I really appreciate any advice! Thanks


Solution

  • The primary problem with adding and removing your "child" view controllers' views as you've done here is that the view controller life cycle methods (viewWillAppear:, viewDidAppear:, etc.) won't ever get called on your child view controllers. Containers like UINavigationController and UITabBarController have always known how to delegate methods like these appropriately to their children, but UIViewController didn't officially support the ability to nest view controllers under your own custom container before iOS 5. It was possible, but it took a lot more work to do it right.

    If you want to stick with the approach of adding and removing subviews, you have two options:

    1. Require iOS 5+, and call addChildViewController:, removeFromParentViewController, transitionFromViewController:toViewController:duration:options:animations:completion:, willMoveToParentViewController:, and didMoveToParentViewController: as described in the Implementing a Container View Controller section of the UIViewController Class Reference.

    2. To support older iOS versions, you'll have to override many of the methods of the UIViewController class and delegate those calls manually to your child view controllers to make them behave as expected. I'd pay particular attention to the sections titled, "Responding to View Events", and "Responding to View Rotation Events" in the UIViewController Class Reference.

    A different approach for pre-iOS 5 support is to present your child view controllers using presentModalViewController:animated: rather than adding their views as subviews to a container. Apple describes this approach in the View Controller Programming Guide for iOS under the section, Creating an Alternate Landscape Interface. The advantage of this approach is that your child view controllers are officially supported as first-class members of the view controller hierarchy, so UIKit will automatically manage their life cycles appropriately. You won't have to override and delegate all those methods manually.