Search code examples
iphoneobjective-cuinavigationcontrollerrotationuiinterfaceorientation

Prevent some view controller in a navigation controller from rotating


I have a navigation controller which displays two view controllers at once. They are layered and the front most controller can be dragged down to reveal the controller below. My app delegate is configured so the app will only allow interface orientations when the view in the back is being revealed, as seen below.

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    if (self.revealNavigationController.isViewRevealed)
    {
        return UIInterfaceOrientationMaskAllButUpsideDown;
    }

    return UIInterfaceOrientationMaskPortrait;
}

Naturally this causes both the front and the back view controller to rotate when the device is rotated but I am only interested in rotating the back view controller. Is there any way to completely disable rotation in the front most view controller?


Solution

  • The solution suggested by axiixc is generally better so depending on your problem, you should look at his answer. However, this did not solve my problem. I had a UINavigationController in which I inserted the view of a view controller as subview at index 0. This way, the view would be in the back. I had then made the UINavigationBar draggable so that when it dragged down, it would reveal the view in the back. The front most view would then move with the navigation bar. I did not manage to get this to work with the view controller containment API introduced iOS 5, as suggested by axiixc.

    Instead, I removed the view from the navigation controller and added it directly to the UIWindow when the app was launched and I handled the rotation of this view myself and disabled rotation on all other views (that is, I disabled it on the navigation controller).

    This is how I added the view in -application:didFinishLaunchingWithOptions:

    self.revealViewController = [[RevealViewController alloc] init];
    [self.window insertSubview:self.revealViewController.view atIndex:0];
    

    Then, right after that I registered for UIDeviceOrientationDidChangeNotification.

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];
    

    My didRotate: looks like this: (this is inspired by another answer on StackOverflow which I am now unable to find again, sorry)

    - (void)didRotate:(NSNotification *)notification
    {
        // Only rotate if the view is revealed
        if (self.revealNavigationController.isViewRevealed)
        {
            UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
            [self updateForDeviceOrientation:orientation animated:YES];
        }
    }
    

    I call -updateForDeviceOrientation:animated:

    - (void)updateForDeviceOrientation:(UIDeviceOrientation)orientation animated:(BOOL)animated
    {
        CGFloat degrees = 0.0f;
        switch (orientation)
        {
            case UIDeviceOrientationLandscapeLeft:
                degrees = 90.0f;
                break;
            case UIDeviceOrientationLandscapeRight:
                degrees = -90.0f;
                break;
            case UIDeviceOrientationPortrait:
                degrees = 0.0f;
                break;
            default:
                break;
        }
    
        CGFloat duration = (animated) ? [UIApplication sharedApplication].statusBarOrientationAnimationDuration : 0.0f;
    
        __weak typeof(self) weakSelf = self;
        [UIView animateWithDuration:duration animations:^{
            // Rotate view
            weakSelf.revealViewController.view.transform = CGAffineTransformIdentity;
            weakSelf.revealViewController.view.transform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(degrees));
    
            // Resize view for rotation
            CGFloat width, height;
            if (UIDeviceOrientationIsLandscape(orientation))
            {
                width = MAX(CGRectGetWidth(weakSelf.revealViewController.view.bounds), CGRectGetHeight(weakSelf.revealViewController.view.bounds));
                height = MIN(CGRectGetWidth(weakSelf.revealViewController.view.bounds), CGRectGetHeight(weakSelf.revealViewController.view.bounds));
            }
            else
            {
                width = MIN(CGRectGetWidth(weakSelf.revealViewController.view.bounds), CGRectGetHeight(weakSelf.revealViewController.view.bounds));
                height = MAX(CGRectGetWidth(weakSelf.revealViewController.view.bounds), CGRectGetHeight(weakSelf.revealViewController.view.bounds));
            }
    
            CGSize newSize = CGSizeMake(width, height);
    
            CGRect viewBounds = weakSelf.revealViewController.view.bounds;
            viewBounds.size = newSize;
            weakSelf.revealViewController.view.bounds = viewBounds;
    
            CGRect viewFrame = weakSelf.revealViewController.view.frame;
            viewFrame.size = newSize;
            weakSelf.revealViewController.view.bounds = viewFrame;
        }];
    }