iOS 6: How do I restrict some views to portrait and allow others to rotate?

Harald Bögeholz picture Harald Bögeholz · Sep 27, 2012 · Viewed 55.6k times · Source

I have an iPhone app that uses a UINavigationController to present a drill-down interface: First one view, then another, up to four levels deep. I want the first three views restricted to portrait orientation and only the last view should be allowed to rotate to landscape. When returning from the fourth view to the third and the fourth view was in landscape orientation I want everything to rotate back to portrait.

In iOS 5 I simply defined shouldAutorotateToInterfaceOrientation: in each of my view controllers to return YES for the allowable orientations. Everything worked as described above, including the return to portrait even if the device was being held in landscape orientation when returning from view controller #4 to #3.

In iOS 6 all view controllers rotate to landscape, breaking those that weren't meant to. The iOS 6 release notes say

More responsibility is moving to the app and the app delegate. Now, iOS containers (such as UINavigationController) do not consult their children to determine whether they should autorotate. [...] The system asks the top-most full-screen view controller (typically the root view controller) for its supported interface orientations whenever the device rotates or whenever a view controller is presented with the full-screen modal presentation style. Moreover, the supported orientations are retrieved only if this view controller returns YES from its shouldAutorotate method. [...] The system determines whether an orientation is supported by intersecting the value returned by the app’s supportedInterfaceOrientationsForWindow: method with the value returned by the supportedInterfaceOrientations method of the top-most full-screen controller.

So I subclassed UINavigationController, gave my MainNavigationController a boolean property landscapeOK and used this to return the allowable orientations in supportedInterfaceOrientations. Then in each of my view controllers' viewWillAppear: methods I have a line like this

    [(MainNavigationController*)[self navigationController] setLandscapeOK:YES];

to tell my MainNavigationController the desired behavior.

Here comes the question: If I now navigate to my fourth view in portrait mode and turn the phone over it rotates to landscape. Now I press the back button to return to my third view which is supposed to work portrait only. But it doesn't rotate back. How do I make it do that?

I tried

    [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait]

in the viewWillAppear method of my third view controller, but it doesn't do anything. Is this the wrong method to call or maybe the wrong place to call it or should I be implementing the whole thing in a totally different way?

Answer

Flo picture Flo · Oct 26, 2012

I had the same problem and found a solution that works for me. To make it work, it is not sufficient to implement - (NSUInteger)supportedInterfaceOrientations in your UINavigationController. You also need to implement this method in your controller #3, which is the first one to be portrait-only after popping controller #4. So, I have the following code in my UINavigationController:

- (BOOL)shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations
{
    if (self.isLandscapeOK) {
        // for iPhone, you could also return UIInterfaceOrientationMaskAllButUpsideDown
        return UIInterfaceOrientationMaskAll;
    }
    return UIInterfaceOrientationMaskPortrait;
}

In view controller #3, add the following:

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

You don't need to add anything to your view controllers #1, #2, and #4. This works for me, I hope it will help you.