3

I only want to support different Orientations on one View in my UINavigationController Stack. How can I do this?

It also has to work in iOS5.

Abizern
  • 146,289
  • 39
  • 203
  • 257
mmackh
  • 3,550
  • 3
  • 35
  • 51

3 Answers3

12

I've had a lot of trouble with how iOS6 handles Orientation, hopefully this is what you're looking for.

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)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation;
-(BOOL)shouldAutorotate;
- (NSUInteger)supportedInterfaceOrientations;

@end

Put this in UINavigationController+autoRotate.m:

#import "UINavigationController+autoRotate.h"

@implementation UINavigationController (autoRotate)

-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return [self.topViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}

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


- (NSUInteger)supportedInterfaceOrientations
{
    if (![[self.viewControllers lastObject] isKindOfClass:NSClassFromString(@"ViewController")])
    {
        return UIInterfaceOrientationMaskAllButUpsideDown;
    }
    else
    {
        return [self.topViewController supportedInterfaceOrientations];
    }
}

@end

For Views that you DO NOT want to rotate, add:

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

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

- (BOOL)shouldAutorotate
{
    return NO;
}

And for Views you DO want to rotate:

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

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAllButUpsideDown;
}

- (BOOL)shouldAutorotate
{
    return YES;
}

In your App's delegate, add:

- (NSUInteger)application:(UIApplication*)application supportedInterfaceOrientationsForWindow:(UIWindow*)window
{
    return UIInterfaceOrientationMaskAllButUpsideDown;
}
mmackh
  • 3,550
  • 3
  • 35
  • 51
  • Why implement `shouldAutorotateToInterfaceOrientation:` when that's a deprecated method? And is "shouldAutoRotate" spelled that way (with upper-case R) in your actual implementation? – Hot Licks Dec 10 '12 at 20:43
  • @HotLicks To ensure compatibility with iOS5, but thanks - shouldAutorotate was a typo – mmackh Dec 11 '12 at 08:16
  • Yes, but you're adding shouldAutorotateTo... to the nav controller and having it look up the stack. For iOS5 the top of the stack would be consulted without this added code. It seems unnecessary (and unnecessarily confusing). – Hot Licks Dec 11 '12 at 12:04
  • FWIW, the default implementation of supportedInterfaceOrientations returns 26 -- all orientations except upside-down. So the method need not be added to VCs for which that default is sufficient. – Hot Licks Dec 11 '12 at 12:09
  • Really helpful. It helped me and saved my time and did within very less time. – AppAspect Apr 01 '13 at 15:07
  • @mmackh, what you're doing above is not subclassing, instead you are creating a category. What you're doing above is a poor programming practice. The reason why overriding those methods in a category is a bad/poor idea is because you'd be bypassing anything that UINavigationController is doing within those methods, and passing their implementation to your own class. By overriding you'll never be able to access the original implementation done by the UINavigationController. By subclassing you are able to call [super someMethod]. You can't do that in a category. – Raz Apr 02 '13 at 22:10
  • Really useful example! As a note i'd add that it also works with UITabBarController, in case someone else is looking for a solution. – RdPC Jun 21 '13 at 14:57
0

I recommend you to NOT create a category on UINavigationController to override those methods. Categories are not aimed to do that, and there is no warranty that your code is going to be loaded instead of Apple's one (even if actually that works). I advise you to create a subclass of UINavigationController, and override those methods in it.

iSofTom
  • 1,718
  • 11
  • 15
  • From the 6.1 Beta release notes: Landscape-only apps that invoke a portrait-only view controller (such as the Game Center login screen) will cause the app to crash. Workaround: Apps should provide the delegate method application:supportedInterfaceOrientationsForWindow: and ensure that portrait is one of the returned mask values. When a UINavigationController is involved, subclass the UINavigationController and override supportedInterfaceOrientations. – mmackh Nov 02 '12 at 07:28
0

The solution does not work on iOS 6 (ok on iOS 5) when:

  • vc A is supporting only portrait orientation
  • vc B is supporting all orientations
  • we push vc B from vc A, rotate vc B (e.g. in landscape) and pop back to vc A. vc A orientation stays in landscape mode...
nicolas
  • 489
  • 1
  • 3
  • 22
  • Seems to work for me on the iPhone 5 & 4 with both iOS 6 and 5 – mmackh Nov 02 '12 at 07:31
  • 1
    Ok I tried again and... I found my mistake, in `supportedInterfaceOrientations`, I was doing `return [self.visibleViewController supportedInterfaceOrientations]` instead of `return [self.topViewController supportedInterfaceOrientations`. It is working like a charm now. Thanks!!! – nicolas Nov 05 '12 at 13:51
  • Great to hear, please don't forget to rate the question & answer if you've found it helpful. – mmackh Nov 05 '12 at 15:02