17

I have an iPhone app I am updating to iOS 6 that is having rotation issues. I have a UITabBarController with 16 UINavigationCotrollers. Most of the subviews can work in portrait or landscape but some of them are portrait only. With iOS 6 things are rotating when they shouldn't.

I tried subclassing the tabBarController to return the supportedInterfaceOrienations of the current navigationController's selected viewController:

- (NSUInteger)supportedInterfaceOrientations{

    UINavigationController *navController = (UINavigationController *)self.selectedViewController;
    return [navController.visibleViewController supportedInterfaceOrientations];
}

This got me closer. The view controller won't rotate out of position when visible, but if I am in landscape and switch tabs the new tab will be in landscape even if it isn't supported.

Ideally the app will only be in the supported orienation of the current visible view controller. Any ideas?

itsji10dra
  • 4,603
  • 3
  • 39
  • 59
Ryan
  • 175
  • 1
  • 1
  • 6

4 Answers4

58

Subclass your UITabBarController overriding these methods:

-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // You do not need this method if you are not supporting earlier iOS Versions
    return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}

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

-(BOOL)shouldAutorotate
{
    return YES;
}

Subclass your UINavigationController overriding these methods:

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

-(BOOL)shouldAutorotate
{
    return YES;
}

Then implement these methods in your viewControllers that you do not want to rotate:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

-(BOOL)shouldAutorotate
{
    return NO;
}

-(NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

And for viewControllers that you do want to rotate:

    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
    {
        return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
    }

    -(NSUInteger)supportedInterfaceOrientations
    {
        return UIInterfaceOrientationMaskAllButUpsideDown;
    }

    -(BOOL)shouldAutorotate
    {
        return YES;
    }

Your tabbarController should be added as the RootviewController of the app window. If you plan to support the default orientations, all but upsidedown is default for iPhone, then you do not need to do anything else. If you want to support upside-down or if you do not want to support another of the orientations, then you need to set the appropriate values in app delegate and/or info.plist.

Venk
  • 5,949
  • 9
  • 41
  • 52
Dean Davids
  • 4,174
  • 2
  • 30
  • 44
  • 7
    This is almost working for me. The problem is if I am already in landscape when I switch tabs to a portrait onl viewy it is still in landscape. Rotating portrait fixes it and it won't rotate back to landscape, but I still need it be in portrait when it first loads. – Ryan Sep 20 '12 at 15:23
  • I am not sure what exactly you need to do to rotate it but I would bet you do it in -(void)viewWillLayoutSubviews. I might not be exactly right on that method name from memory. My own views, where I used this code, are changing completely when rotated and I use that method to reconfigure them back to portrait mode. You also might try something in -viewWillDisappear. Maybe [self.view setNeedsDisplay]. I am not at Xcode at the moment so these are just ideas that I would explore in your case. – Dean Davids Sep 21 '12 at 12:13
  • almost what i needed. i still have some issue with it. in iphone everything is fine using your code. i want the ipad UI to be only landscape. how can one achieve that keeping ios 6 and 4.3 in mind? – Hashmat Khalil Sep 27 '12 at 19:34
  • 2
    I'm not arguing this answer will work, but we shouldn't need it. According to Apple's docs, it should work properly out of the box. http://developer.apple.com/library/ios/documentation/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/TabBarControllers.html#//apple_ref/doc/uid/TP40011313-CH3-SW26 – DBD Sep 28 '12 at 17:00
  • @Ryan did you solved the tab switching rotation problem? I am struggling with it now :( – Ahmed Al Hafoudh Oct 13 '12 at 13:48
  • As an update- Once I gave up iOS5 support, I was able to delete all this code except shouldAutorotate. – Dean Davids Jul 19 '13 at 00:32
  • @Ryan I have a same problem. While there is a decision in a look if(UIInterfaceOrientationIsLandscape(self.interfaceOrientation)){ [[UIDevice currentDevice] performSelector:NSSelectorFromString(@"setOrientation:") withObject:(id)UIInterfaceOrientationPortrait]; } but this code of Apple can not accept – Sergei S Oct 21 '13 at 08:48
5

I had issue that some View controllers in the navigation stack support all the orientations, some only portrait, but UINavigationController was returning all app supported orientations, this little hack helped me. I'm not sure if this is intended behavior or what

@implementation UINavigationController (iOS6OrientationFix)

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

@end
Venk
  • 5,949
  • 9
  • 41
  • 52
Mindaugas
  • 1,707
  • 13
  • 20
  • I do not understand how that hack helped you having different behaviors on different views controller of one navigation controller. As fas as I know this hack will be executed just once, so all view controllerd will have same rotation behavior than topViewController. Am I missing something?? – M Penades Sep 21 '12 at 12:04
  • 1
    this code is executed every time device changes orientation, so this will return supported orientations for which ever uiviewcontroller is currently active and visible at the moment – Mindaugas Sep 22 '12 at 20:24
3

I think is better something like that (as a category method)

-(NSUInteger) supportedInterfaceOrientations {
    if([self.topViewController respondsToSelector:@selector(supportedInterfaceOrientations)])
    {
        return [self.topViewController supportedInterfaceOrientations];
    }
    return UIInterfaceOrientationMaskPortrait;
}

this ensures that the method is implemented. If you aren't doing this check and the method is not implemented (like in iOS5 env) the app should crash!

Alvise Susmel
  • 569
  • 4
  • 9
0

If you plan to enable or disable rotation for all view controllers you don't need to subclass UINavigationController. Instead use:

   -(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window 

in your AppDelegate.

If you plan to support all orientations in your app but different orientations on PARENT View Controllers (UINavigationController stack for example) you should use

   -(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window 

from AppDelegate in combination with the following methods in your PARENT View Controller.

    - (BOOL)shouldAutorotate

and

- (NSUInteger)supportedInterfaceOrientations

But if you plan to have different orientation settings in different CHILDREN ViewControllers in the same navigation stack (like me) you need to check the current ViewController in the navigation stack.

I've created the following in my UINavigationController subclass:

    - (BOOL)shouldAutorotate
{
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations
{
    int interfaceOrientation = 0;

    if (self.viewControllers.count > 0)
    {
        DLog(@"%@", self.viewControllers);
        for (id viewController in self.viewControllers)
        {
            if ([viewController isKindOfClass:([InitialUseViewController class])])
            {
                 interfaceOrientation = UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
            }
            else if ([viewController isKindOfClass:([MainViewController class])])
            {
                 interfaceOrientation = UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
            }
            else
            {
                 interfaceOrientation = UIInterfaceOrientationMaskAllButUpsideDown;
            }
        }
    }
    return interfaceOrientation;
}

Since you cannot control anymore from children ViewControllers the rotation settings of presented view controller you must somehow intercept what view controller is currently in the navigation stack. So that's what I did :). Hope that helps !

Venk
  • 5,949
  • 9
  • 41
  • 52
Razvan
  • 4,122
  • 2
  • 26
  • 44