Rotation behaving differently on iOS6
Asked Answered
F

12

14

I did an App which is tab-based. Nothing needs to be on landscape mode but a couple of views. It worked OK on iOS5 and I was pretty happy with the result. However with iOS6 and without messing with anything, it now rotates all the views and the consequences are not nice.

Because its a tab-based app, the couple of view I need in landscape are modalViews. That way I didn't mess with the tabbar and I had only to chose portrait in the "Supported Orientations" setting on build options and set:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {

    return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
    return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}

on the views I wanted landscape.

Now with iOS6 this views are also on portrait mode, no matter what and they do not show the landscape mode even if I rotate the device. Likewise, if I allow all the orientations on the "Supported orientations", they all rotate, no matter what I put on the method above.

On all the views I haven't check the box "Use Autolayout" on storyboards.

Any help here?

*EDIT** Now that I see it, the App I have on the device works fine. I've installed with a promo code, not from Xcode, only to see whether my customers are having problems or not. Fortunatelly they are not. The problem remains though.

Fleshly answered 21/9, 2012 at 19:1 Comment(4)
The more stylish (and correct) way to express that shouldAutorotate... implementation is return UIInterfaceOrientationIsLandscape(interfaceOrientation);.Guarded
Be aware that this problem is caused by building using Xcode 4.5 AND running the app on iOS6. If you're existing version of the app in the app store was built with an earlier version of Xcode your customers will not see the problem even on iOS6, nor will you by using a promo codeOvotestis
@Martin, thanks. After a couple of hours of total panic, I tried a promo code and I saw that everything was fine with the current version.Fleshly
@Marcal: if the code sample above is real, note that the second return will never get called, since it will always return on the first line.Campbell
F
36

The most important part of the documentation I found for this issue is:

When the user changes the device orientation, the system calls this method on the root view controller or the topmost presented view controller that fills the window

To make my app fully working for autorotation in iOS 6, I had to do the following:

1) I created a new subclass of UINavigationController, and added shouldAutorotate and supportedInterfaceOrientation methods:

// MyNavigationController.h:
#import <UIKit/UIKit.h>

@interface MyNavigationController : UINavigationController

@end

// MyNavigationController.m:
#import "MyNavigationController.h"
@implementation MyNavigationController
...
- (BOOL)shouldAutorotate {
    return YES;
}

- (NSUInteger)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskAll;
}
...
@end

2) In the AppDelegate, I did use my new subclass to show my root ViewController (it is introScreenViewController, a UIViewController subclass) and did set the self.window.rootViewController, so it looks that:

    nvc = [[MyNavigationController alloc] initWithRootViewController:introScreenViewController];
    nvc.navigationBarHidden = YES;
    self.window.rootViewController = nvc;
    [window addSubview:nvc.view];
    [window makeKeyAndVisible];
Flyblown answered 22/9, 2012 at 3:11 Comment(6)
Got it!! It was exactly that. However, since I do as much as I can in Storyboard, it was enough to set the class of the tabbarcontroller to the subclass I created following your suggestion. I din't have to mess with AppDelegate. Thanks a lot for the help. (To all of you).Fleshly
I, too, found it was sufficient to set the class in the storyboard to my new class. Thanks!Radmilla
auto-rotation completely broke on my app for iOS 6, this fix worked perfectly. I just had to set self.window.rootViewController (was already adding the subview), and my old pre-iOS6 code worked.Muggy
This solution also works for the storyboard. Just set the class of navigation controller to custom navigation controller. It just works.Mindful
This is awesome. Thanks!! :) I had a TabBarController as root VC in a window-based app, and Apple's doc for supporting autorot in 6.0+ affected nothing. Whatever you use the above code for, you will get several compile errors in the .m lines. I solved it by casting nvc (to UIViewController) and hiding the extra navigationbar with the more correct [nvc setNavigationBarHidden:YES animated:NO];Crossgrained
Just like William Denniss wrote: iOS6 + Xcode 4.6.1 also broke autorotation in my app (previously released for ios3). All I had to to was add self.window.rootViewController = myNavController; and autorotation worked againMargetmargette
A
10

This is the alternative solution for iOS6 in case you are using the tab bar controller. It also shows that is NOT needed to override UINavigationController or even UITabBarController.

In your xyzAppDelegate.h add this interface:

@interface UITabBarController (MyApp)
@end

And in xyzAppDelegate.m add these methods:

@implementation UITabBarController (MyApp) 

-(BOOL)shouldAutorotate
{
  return YES;
}

- (NSUInteger)supportedInterfaceOrientations
{
  // your custom logic for rotation of selected tab
  if (self.selectedIndex==...) {
    return UIInterfaceOrientationMaskAll;
  } 
  else {
    return UIInterfaceOrientationMaskPortrait|UIInterfaceOrientationMaskPortraitUpsideDown;
  }
}

@end

Also, set the root view controller for the app window:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  ...
  [self.window setRootViewController:tabBarController];
Austerity answered 24/9, 2012 at 10:57 Comment(2)
I was able to get this working when using storyboards without have to set the root view controller. I also just set the supportedInterfaceOrientations to the default and then override for individual controllers as required. I also had to set all possible orientations in the Info.plist.Butane
This is probably good but not tried by me, because I don't have a separate controller source. Just a tab bar controller in IB set to be root VC.Crossgrained
G
6

Are you setting the rootViewController in the delegate? For example,

    self.window.rootViewController = self.navigationController;

When I was doing some iOS6 testing it wouldn't work properly until I did that...

Garling answered 21/9, 2012 at 19:17 Comment(4)
No, I'm not. But the modal views I want the landscape modes are not embeded into a navigation controllerFleshly
well, from what I remember, something has to be set to the rootViewControllerGarling
Well, now that I see it, I set UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController; on applicationDidFinishLaunching...Fleshly
The generic solution to the console message that a root VC is missing is to (if your root VC var is called rootVC) set it to _rootVC in did Finish. This is independent from how you built your app.Crossgrained
W
5

I have a good solution for cross 5.0 to 6.0 working - All of the above with

-(BOOL)shouldAutorotate{return [self shouldIRotateAnyiOS];}//iOS6

-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{return [self shouldIRotateAnyiOS];}//pre iOS6

-(BOOL)shouldIRotateAnyiOS{
UIInterfaceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation];
//do the rotation stuff
return YES
}
Weintrob answered 4/10, 2012 at 5:8 Comment(1)
A problem in tabbed window-based apps is that shouldAutorotate never fires anywhere. But if it fires in your app, this could work well.Crossgrained
R
1

You might double check Support Interface Orientations

enter image description here

On previous version, it means nothing, but affects whole application now.

Note: The 'upside down' option doesn't work even enabled or disabled on iOS 6.

Reinwald answered 26/9, 2012 at 9:22 Comment(5)
i have my upside down checked. the app targeted to iOS 5 but running on iOS 6 does not work with this as the only solution.Identify
If you want to support all orientations, try returning true for the shouldAutorotateToInterfaceOrientation function like as (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return true; }Reinwald
in fact, i already had that implemented in every view-controller in my app. the only thing that works for me is the combination of answers having to do with creating a category on the rootViewControllers of my app and implementing the overrides as mentioned above.Identify
You are right, this doesn't work for the upside down case. Updated the answer.Reinwald
I was updating a very old project (iOS4x) recently... This was killing me. None of the orientations were defined in the plist!. This + @Flyblown 's answer made it work :D Thanks.Accordance
A
1

This is what works for me.

I created a new subclass of UINavigationController, and added shouldAutorotate and supportedInterfaceOrientation methods:

#import "MyNavigationController.h"

@interface MyNavigationController ()

@end

@implementation MyNavigationController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

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

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

@end

Then add this to your delegate

UINavigationController *nvc = [[MyNavigationController alloc] initWithRootViewController:_viewController];
nvc.navigationBarHidden = NO; // YES if you want to hide the navigationBar
self.window.rootViewController = nvc;
[_window addSubview:nvc.view];
[_window makeKeyAndVisible];

Now you can add this to the views you want to rotate in all orientations

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

-(NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAll;
}

-(BOOL)shouldAutorotate
{
    return YES;
}

Or add this to the views you only want to go portrait and portraitUpsideDown

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

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown ;
}

- (BOOL)shouldAutorotate
{
    return YES;
}
Audly answered 7/11, 2012 at 10:8 Comment(0)
R
0

Autorotation changed in iOS 6.0. Check this link for more information.

Autorotation is changing in iOS 6. In iOS 6, the shouldAutorotateToInterfaceOrientation: method of UIViewController is deprecated. In its place, you should use the supportedInterfaceOrientations and shouldAutorotate methods.

Raina answered 21/9, 2012 at 19:11 Comment(4)
by setting return UIInterfaceOrientationMaskLandscape; on the landscape views. it fixes this orientation for that view, but the rest of the application rotates.Fleshly
Have you implemented supportedInterfaceOrientations?Raina
Yeah, on one of the view controllers under the tabBar and on the tobe-landscape views.Fleshly
I followed this article and made the recommended changes. No change, and none of the new methods are fired. So it's incomplete or not generic. I followed the accepted answer, and everything worked without any further code changes.Crossgrained
G
0

There are a few things you may need to handle to get this working since with iOS6 the structure of autorotate has changed. The structure of how autorotate is determined is now reversed. It used to be that an individual view controller can control the autorotate with its decision but now the "shouldAutorotate" is determined by the highest parent in the navigation which in your case is the tabBar.

  1. You need to make sure your window has a rootViewController set and not just added as a subview.
  2. You may need to subclass your tabBarController to implement both "supportedInterfaceOrientations" and "shouldAutorotate".
  3. If there are any viewControllers that need to behave differently then you will need to have your tabBarController consult with them for the answer on whether they should autorotate.

for example:

- (BOOL)shouldAutorotate
{
    return self.selectedViewController.shouldAutorotate;
}

and in your view controller you would implement shouldAutorotate and make the decision there.

Gaw answered 21/9, 2012 at 20:58 Comment(0)
S
0

This code common for ios5 and ios6

-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
    if (UIInterfaceOrientationIsLandscape(interfaceOrientation)) {
        [self performSelector:@selector(setframeLandscap) withObject:nil afterDelay:0.2];
    }
    else {
        [self performSelector:@selector(setframePortrait) withObject:nil afterDelay:0.2];
    }
}

-(BOOL)shouldAutorotate {    
    return YES;
}
Siler answered 26/9, 2012 at 8:34 Comment(0)
T
0

From Apple's documentation for shouldAutorotateToInterfaceOrientation:

Override the supportedInterfaceOrientations and preferredInterfaceOrientationForPresentation methods instead.

http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/DeprecationAppendix/AppendixADeprecatedAPI.html#//apple_ref/occ/instm/UIViewController/shouldAutorotateToInterfaceOrientation:

Tench answered 13/10, 2012 at 9:16 Comment(0)
E
0

For app "Same Picture" on iOS6 I need an orientation change, my UIViewController never be informed for orientation, it's an photo overlay likely didrotate works well :

- (void)didRotate: ( NSNotification* )note
{
    [self performSelector:@selector(rotateRecalculDiffere) withObject:nil afterDelay:0.3 ];
}

I make fine size adjust with delayed call. From notification it's easy to know final orientation

Emotion answered 1/11, 2012 at 10:5 Comment(0)
P
0

Did a little experimentation: Took an existing app (that wouldn't rotate in iOS-6, but did previously) and added the one line self.window.rootViewController = navCtlr; to AppDelegate. This resulted in an app that appears (at least at first blush) to rotate just fine.

Then, out of curiosity, I created the RotationCanary class and plugged an instance of that into self.window.rootViewController. I'd start up the app and wait for the inevitable "unrecognized selector", create a new method of that name in RotationCanary, and re-run. The new method would call the real nav ctlr and log its response before returning it. This produced (sooner than I expected) the following log:

2012-12-07 13:08:47.689 MyTestApp[53328:c07] System Version is 6.0;    Supported versions are 5.0.x to 6.0.x
2012-12-07 13:08:47.691 MyTestApp[53328:c07] Host memory (in bytes) used: 3489513472 free: 803893248 total: 4293406720
2012-12-07 13:08:47.692 MyTestApp[53328:c07] Memory in use by task (in bytes): 23719936
2012-12-07 13:08:47.695 MyTestApp[53328:c07] Creating database
2012-12-07 13:08:47.699 MyTestApp[53328:c07] Item Selected: (null)  (null)
2012-12-07 13:08:47.700 MyTestApp[53328:c07] <DetailViewController.m:(27)> Entering Method -[DetailViewController viewDidLoad]
2012-12-07 13:08:47.706 MyTestApp[53328:c07] <SplitContentViewController.m:(57)> Entering Method -[SplitContentViewController viewDidLoad]
2012-12-07 13:08:47.708 MyTestApp[53328:c07] <FamilyMasterViewController.m:(32)> Entering Method -[FamilyMasterViewController viewDidLoad]
2012-12-07 13:08:47.709 MyTestApp[53328:c07] <MasterViewController.m:(41)> Entering Method -[MasterViewController viewDidLoad]
2012-12-07 13:08:47.718 MyTestApp[53328:c07] <FamilyHomeDetailViewController.m:(51)> Entering Method -[FamilyHomeDetailViewController viewDidLoad]
2012-12-07 13:08:47.820 MyTestApp[53328:c07] -[RotationCanary _preferredInterfaceOrientationGivenCurrentOrientation:] - current = 2, result = 2
2012-12-07 13:08:47.821 MyTestApp[53328:c07] -[RotationCanary _existingView] - view = (null)
2012-12-07 13:08:47.824 MyTestApp[53328:c07] -[RotationCanary view] - view = <UILayoutContainerView: 0x9c987f0; frame = (0 0; 768 1024); autoresize = W+H; layer = <CALayer: 0x9c8fa00>>
2012-12-07 13:08:47.825 MyTestApp[53328:c07] -[RotationCanary view] - view = <UILayoutContainerView: 0x9c987f0; frame = (0 0; 768 1024); autoresize = W+H; layer = <CALayer: 0x9c8fa00>>
2012-12-07 13:08:47.826 MyTestApp[53328:c07] -[RotationCanary view] - view = <UILayoutContainerView: 0x9c987f0; frame = (0 0; 768 1024); autoresize = W+H; layer = <CALayer: 0x9c8fa00>>
2012-12-07 13:08:47.827 MyTestApp[53328:c07] -[RotationCanary wantsFullScreenLayout] - result = YES
2012-12-07 13:08:47.827 MyTestApp[53328:c07] -[RotationCanary view] - view = <UILayoutContainerView: 0x9c987f0; frame = (0 0; 768 1024); autoresize = W+H; layer = <CALayer: 0x9c8fa00>>
2012-12-07 13:08:47.830 MyTestApp[53328:c07] -[RotationCanary _tryBecomeRootViewControllerInWindow:] - window = <UIWindow: 0x9c76320; frame = (0 0; 768 1024); opaque = NO; autoresize = RM+BM; layer = <UIWindowLayer: 0x9c76450>>, result = YES
2012-12-07 13:08:47.916 MyTestApp[53328:c07] -[RotationCanary _deepestDefaultFirstResponder] - result = <SignOnViewController: 0x9c942a0>
2012-12-07 13:08:47.916 MyTestApp[53328:c07] Device model: x86_64

Curiously, the class was never actually invoked to perform rotation -- only during setup.

I suspect that Apple uses the setting of rootViewController purely as a way to indicate that the app has been modified for iOS 6 rotation -- it has no real function otherwise.

FWIW: It occurred to me that the caller might be using respondsToSelector and skipping some calls, so I added an implementation of resolveInstanceMethod: to RotationCanary, to trap any such attempts. None occurred.

Pathognomy answered 7/12, 2012 at 19:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.