48

I have been scouring the internet for a solution to this but am finding nothing. I am trying to make my iOS 5 app iOS 6 compatible. I cannot get the orientation stuff to work right. I am unable to detect when a rotation is about to happen. Here is the code I am trying:

- (BOOL)shouldAutorotate {
    return NO;
}

- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait;
}
// pre-iOS 6 support
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}

The new supportedInterfaceOrientation: method gets called just fine. The shouldAutorotate method, however, will not fire. I need to do some image swapping on rotate, but I can't get any indication that a rotation is about to occur.

Thanks in advance.

enagra
  • 2,296
  • 1
  • 18
  • 19
Lizza
  • 2,769
  • 5
  • 39
  • 72
  • This is answered in here: http://stackoverflow.com/questions/12260261/shouldautorotatetointerfaceorientation-not-being-called-in-ios-6/12580217#12580217 See it :D – KarenAnne Oct 08 '12 at 06:10
  • @KarenAnne, that answer doesn't fix the issue of 'shouldAutorotate' not being called in the first place. Also, 'shouldAutorotateToInterfaceOrientation' is deprecated in iOS 6, so it shouldn't be considered a valid option going foward. – Daniel J Aug 31 '15 at 18:18

9 Answers9

80

See if you are getting the following error when your App starts.

Application windows are expected to have a root view controller at the end of application launch

If so the way to fix it is by making the following change in the AppDelegate.m file (although there seem to be a number of answers how to fix this):

// Replace
[self.window addSubview:[navigationController view]];  //OLD

// With
[self.window setRootViewController:navigationController];  //NEW

After this shouldAutoRotate should be correctly called.

GabLeRoux
  • 16,715
  • 16
  • 63
  • 81
user1672376
  • 846
  • 1
  • 6
  • 4
  • 1
    I had the same problem before but was able to fix it by doing something like this: self.window.rootViewController = navigationController; (which is the same as @user1672376's answer) – yoninja Feb 07 '13 at 09:52
  • yes I was trying hard to get shouldAutoRotate method to call but it was not working as soon as I changed the above piece of code it worked perfectly. This is the correct answer I was searching it lot. – ashish Feb 21 '13 at 16:42
  • But if you're not getting that error, and neither do you want to set some random UIViewController as the root view controller... – Daniel J Aug 31 '15 at 18:19
45

When using UINavigationController as the basis for an app I use the following subclass to give me the flexibility to allow the top most child viewcontroller to decide about rotation.

@interface RotationAwareNavigationController : UINavigationController

@end

@implementation RotationAwareNavigationController

-(NSUInteger)supportedInterfaceOrientations {
    UIViewController *top = self.topViewController;
    return top.supportedInterfaceOrientations;
}

-(BOOL)shouldAutorotate {
    UIViewController *top = self.topViewController;
    return [top shouldAutorotate];
}

@end
Mike Pollard
  • 10,195
  • 2
  • 37
  • 46
  • 5
    This is by far the easiest and most elegant solution to allowing each ViewController in a NavigationController to have their own supported interface orientations. – Gallonallen Sep 17 '13 at 19:06
  • This is indeed a great solution and can be used with SplitViewControllers as well with a little tweaking. – vincentjames501 Mar 14 '14 at 20:52
  • To be more clear, create such a sub-class and then set it as the custom class for your navigation controller in storyboard Identity Inspector. – tedyyu May 26 '14 at 16:15
  • Great answer thanks! Return type of supportedInterfaceOrientations should be changed from NSUInteger to UIInterfaceOrientationMask to remove a warning. – Patrick Domegan Jan 27 '17 at 18:31
  • Note: for this to work on iPad, you need to opt out of multitasking: – Patrick Domegan Jan 27 '17 at 19:04
15

That method is not the correct way to determine that. The correct method is willRotateToInterfaceOrientation:duration:

The should rotate to orientation (as opposed to shouldAutorotate) method is deprecated and will no longer be called as of iOS 6, but it was not meant to be used the way you were using it anyway.

EDIT Response to repeated downvotes. Please explain why using the method I indicated is not an (to quote OP) "indication that a rotation is about to occur." The content of the question and the title are mismatched.

borrrden
  • 33,256
  • 8
  • 74
  • 109
  • 2
    -(BOOL)shouldRotate has appeared in iOS 6, however, it is never called. How is one meant to have a view controller that rotates only some of the time now (for instance having a lock button to protect against rotations)? – Daniel Wood Oct 09 '12 at 12:35
  • 14
    I figured out why my shouldAutorotate was not getting called in iOS 6. It was due to my view controller being a child of a UINavigationController and it appears that the nav controller doesn't delegate the -shouldAutorotate method to it's topViewController like the previous behaviour. You can get around this by subclassing the UINavigationController and overriding the -shouldAutorotate and -supportedIntervalOrientations methods. – Daniel Wood Oct 09 '12 at 13:16
  • Yeah, it's a new way of doing things...only presented and root view controllers get the calls. The reasoning is that child view controllers usually just resize to fit the frames of their parents anyway. However, the wilRotate and didRotate methods are forwarded to child controllers by default. – borrrden Oct 09 '12 at 14:03
  • Subclassing UINavigationController is not recommended. A category is better see http://stackoverflow.com/questions/12520030/how-to-force-a-uiviewcontroller-to-portait-orientation-in-ios-6 – possen Oct 13 '12 at 02:10
  • 1
    @possen Out of curiousity, why do you say that? To me it seems like a category would be a stranger choice since it will delete an existing implementation and make it unreachable (compiler warning will be produced) – borrrden Oct 13 '12 at 06:29
  • 2
    There's a thread on the Apple forums where the Apple rep explicitly recommends *against* making a category, but subclassing instead. https://devforums.apple.com/message/744384 – Mike M Nov 19 '12 at 22:38
  • WEll it doesn't work for me for UIImagePickerController. I tried to extend it (as it extends UINavigationController) and implement the method supportedIntervalOrientation and shouldAutoRotate but it wouldn't help – Dejell Jan 21 '13 at 17:50
  • 12
    This answer is completely wrong, shouldAutorate is not deprecated, In fact it's only available on IOS 6.0 and it's the correct way to determine if the screen should or not rotate. the willRotateToInterfaceOrientation:duration: yes called when the screen will be rotate, not do determine if should or not rotate. – Guilherme Torres Castro Feb 28 '13 at 11:57
  • This answer looks like it has been edited since I answered...I believe before it only had the old version of shouldRotate (shouldAutorotateToInterfaceOrientation). But my answer is still not "completely wrong" because OP is looking for (in his words) "detect when a rotation is about to happen". That is exactly what the willRotate method is for. – borrrden Feb 28 '13 at 14:18
10

It looks that on iOS 6 the container navigations controller doesn't consult child view controllers when rotating:

in iOS 6 release notes :

Now, iOS containers (such as UINavigationController) do not consult their children to determine whether they should autorotate. By default, an app and a view controller’s supported interface orientations are set to UIInterfaceOrientationMaskAll for the iPad idiom and UIInterfaceOrientationMaskAllButUpsideDown for the iPhone idiom.

This behavior is easy to test. What I did is to use the same custom view controller

  1. first case as a main view controller
  2. second case as a child of a UIPageViewController

In the first case everything is decided in the custom navigation controller by the combination of shouldAutorotate and supportedInterfaceOrientations given that supportedInterfaceOrientations agrees with the supported orientations of the application.

In the second case even if the supportedInterfaceOrientations of the custom view controller is called by the UIPageViewController the return value is not taken in to consideration. It works if the two methods are overwritten in a subclass of the UIPageViewController. I am not sure about the side effects of that as this class is not supposed to be subclassed.

Adrian
  • 1,595
  • 1
  • 19
  • 21
10

If your viewController is a child viewController in a UINavigationController then you can do the following:

  • Subclass UINavigationController
  • override shouldAutoRotate in your subclass
  • send your topViewController this message when this method get called

// This Method is inside your UINavigationController subclass

- (BOOL)shouldAutorotate
{
    if([self.topViewController respondsToSelector:@selector(shouldAutorotate)])
    {
        return [self.topViewController shouldAutorotate];
    }
    return NO;
}
  • Now your viewControllers will respond respectively to this method.
  • Note, that you can do the same with other orinetaion-methods
Basheer_CAD
  • 4,908
  • 24
  • 36
1

I was also getting the following error when your App starts.

"Application windows are expected to have a root view controller at the end of application launch"

I am using a UISplitViewController *splitViewController

If so the way to fixed it is by making the following change in the AppDelegate.m file:

Replace

 [self.window addSubview:[splitViewController view]];

With

[self.window setRootViewController:splitViewController];

After this shouldAutoRotate was called and worked correctly.

Wouter J
  • 41,455
  • 15
  • 107
  • 112
1

I'm using iOS 7 but I believe my case may be helpful to others.

I have a deep view controller hierarchy rooted with UITabBarController. The only place where shouldAutorotate is guaranteed to be called is inside the UITabBarController. So I simply subclass UITabBarController and put my rotation control logic inside my shouldAutorotate method.

Golden Thumb
  • 2,531
  • 21
  • 20
0

This is how I would do it

if you want to check which is current orientation then add this line to your viewconrtoller.m file

 #define isPortrait [[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortrait || [[UIApplication sharedApplication] statusBarOrientation] == UIInterfaceOrientationPortraitUpsideDown

then where you want to check orientation ,write condition like below

- (void)viewDidLoad
{

        if(isPortrait)
        {
            //portrait mode....

            NSLog(@"its in IsPotraitMode");
        }
        else
        {
            //landscape mode....
            NSLog(@"its in IsLandscapeMode");
        }
}
Sam B
  • 27,273
  • 15
  • 84
  • 121
  • No, viewDIDLoad is too early: the VC doesn't know about its views yet. Do it in viewWillAppear, when self.view knows its current frame. – Bill Cheswick Jul 28 '13 at 14:57
0

If you use UINavigationController as the basis for an app. I create a category of UINavigationController and call it "UINavigationController+autoRotate". Put this in your UINavigationController+autoRotate.h:

#import <UIKit/UIKit.h>

@interface UINavigationController (autoRotate)

-(BOOL)shouldAutorotate;
-(NSUInteger)supportedInterfaceOrientations;

@end

Put this in UINavigationController+autoRotate.m:

#import "UINavigationController+autoRotate.h"

@implementation UINavigationController (autoRotate)

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

- (NSUInteger)supportedInterfaceOrientations
{
    return [self.topViewController supportedInterfaceOrientations];
}

@end
Vladyslav Panchenko
  • 1,517
  • 1
  • 19
  • 23