8

I have a UIViewController, which is presented modally (full screen) and I would like to disable autorotation in that view. I do not want to restrict it to landscape or portrait, just would like it to stay in whatever orientation it was originally presented.

On iOS 6 it was sufficient to just override the method:

- (BOOL)shouldAutorotate {
    return NO;
}

And it did exactly what I wanted. On iOS 7 however, this seems to have no effect. The method does get called, but the return value seems to be ignored by the OS - it auto rotates no matter what.

The documentation does not mention any changes to this method. How can I achieve the desired effect on iOS 7?

Edit: the view controller is being presented (not pushed!) by a UINavigationViewController:

[self.navigationController presentViewController:vc animated:YES completion:nil];

Solution:

As odd as it may seem, but this solution was not published in the numerous existing questions on this topic. On iOS 7 it seems the answer the UINavigationController gives to shouldAutorotate is what the OS acts on. We need to subclass UINavigationController to modify its behaviour.

When dealing with a regular navigation stack it is indeed sufficient to just use [self.topViewController shouldAutorotate], but when there is modal view, it resides in self.presentedViewController, not self.topViewController. Thus the full solution looks like:

- (BOOL)shouldAutorotate {
    UIViewController *vc;
    if (self.presentedViewController) vc = self.presentedViewController;
    else vc = [self topViewController];
    return [vc shouldAutorotate];
}
SaltyNuts
  • 5,068
  • 8
  • 48
  • 80
  • iOS7 uses the view controller method `supportedInterfaceOrientations` to control the autorotation process, much as earlier versions used `shouldAutorotateToInterfaceOrientation:` You may also run into issues if you're presenting the view controller in a `UITabController`. – David Berry Mar 28 '14 at 15:23
  • Is your `UIViewController` being presented in a `UINavigationController`? – Craig Siemens Mar 28 '14 at 15:23
  • Possible duplicate of http://stackoverflow.com/questions/12520030/how-to-force-a-uiviewcontroller-to-portait-orientation-in-ios-6 – Isaiah Turner Mar 28 '14 at 15:41
  • It is not a duplicate of that question. That question asks about doing this in iOS 6, but my question is about code that worked perfectly in iOS 6 but gives problems under iOS 7. – SaltyNuts Mar 28 '14 at 16:04

2 Answers2

5

So I just tried you code and it worked which leads me to believe that you are presenting your UIViewController in a UINavigationController. For whatever reason, iOS 7 changed how UINavigationController handle rotations.

The easiest solution is to create a subclass of UINavigationController that overrides the shouldAutorotate method and returns the value from the topViewController.

@interface CustomNavigationController : UINavigationController

@end

@implementation CustomNavigationController

- (BOOL)shouldAutorotate
{
    return [[self topViewController] shouldAutorotate];
}

@end

So instead of doing this, where viewController is your object that return NO for shouldAutorotate.

UINavigaitonController *navController = [UINavigationController alloc] initWithRootViewController:viewController];
[self presentViewController:navController animated:YES completion:nil];

You would use the CustomNavigationController instead

CustomNavigationController *customNavController = [CustomNavigationController alloc] initWithRootViewController:viewController];
[self presentViewController:customNavController animated:YES completion:nil];
Craig Siemens
  • 12,942
  • 1
  • 34
  • 51
  • Thanks for the tip, but this answer is only partial. This does not work correctly when `self.presentedViewController` is set, because `self.presentedViewController` != `self.topViewController` – SaltyNuts Mar 28 '14 at 16:02
  • In my case, `viewController` is not embedded into another `navController`. It's presented full screen with no navBar. I converted the main `navController` to a subclass as you suggested, but modified the overrides `shouldAutorotate` to check for presented view controller. – SaltyNuts Mar 28 '14 at 16:14
-1
#import <objc/message.h>

-(void)viewDidAppear:(BOOL)animated{             

    objc_msgSend([UIDevice currentDevice], @selector(setOrientation:), UIInterfaceOrientationPortrait );

 }

see How do I programmatically set device orientation in iOS7?

But take the case you are using this method because it is a private API, and your app may be rejected by Apple. So, maybe is better to set orientation from Project details -> General -> Deployment Info tab, choose Landscape Left and Landscape Right choice only. This can be a better approach, if all your views need only one kind of orientation.

Community
  • 1
  • 1
ares777
  • 3,590
  • 1
  • 22
  • 23
  • 1
    Calling private APIs can get your app rejected. – Craig Siemens Mar 28 '14 at 15:20
  • You don't pay attention. obj/message.h is actually included in Apple's examples, see http://www.opensource.apple.com/source/objc4/objc4-371.1/runtime/message.h – ares777 Mar 28 '14 at 15:25
  • 1
    I know `objc_msgSend` is fine. Calling `setOrientation:` is the issue because the orientation property on `UIDevice` is declared as read only. – Craig Siemens Mar 28 '14 at 15:28
  • Clarifying that is a private API is better than down voting it. This IS an answer to the OP's question. For many people, this may even be an acceptable solution if they do not plan to submit their app for App Store review. – Isaiah Turner Mar 28 '14 at 15:33
  • I used it and my app passed Apple's verification process. – ares777 Mar 28 '14 at 15:33
  • @user3344236 Please update your answer to include the fact that it is a private API and people may remove their downvotes. – Isaiah Turner Mar 28 '14 at 15:35
  • 1
    Well, I do not want it to be `UIInterfaceOrientationPortrait` - I want whatever the device was in when the view was presented. – SaltyNuts Mar 28 '14 at 15:36