iDev: iOS 6 Orientataion Problem

Here the solution:

From Apple’s iOS 6 SDK Release Notes:

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

More responsibility is moving to the app and the app delegate. 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.

A view controller’s supported interface orientations can change over time—even an app’s supported interface orientations can change over time. The system asks the top-most full-screen view controller (typically the root view controller) for its supported interface orientations whenever the device rotates or whenever a view controller is presented with the full-screen modal presentation style. Moreover, the supported orientations are retrieved only if this view controller returns YES from its shouldAutorotate method. The system intersects the view controller’s supported orientations with the app’s supported orientations (as determined by the Info.plist file or the app delegate’s application:supportedInterfaceOrientationsForWindow: method) to determine whether to rotate.

The system determines whether an orientation is supported by intersecting the value returned by the app’s supportedInterfaceOrientationsForWindow: method with the value returned by the supportedInterfaceOrientations method of the top-most full-screen controller. The setStatusBarOrientation:animated: method is not deprecated outright. It now works only if the supportedInterfaceOrientations method of the top-most full-screen view controller returns 0. This makes the caller responsible for ensuring that the status bar orientation is consistent.

For compatibility, view controllers that still implement the shouldAutorotateToInterfaceOrientation: method do not get the new autorotation behaviors. (In other words, they do not fall back to using the app, app delegate, or Info.plist file to determine the supported orientations.) Instead, the shouldAutorotateToInterfaceOrientation: method is used to synthesize the information that would be returned by the supportedInterfaceOrientations method.

If you want your whole app to rotate then you should set your Info.plist to support all orientations. Now if you want a specific view to be portrait only you will have to do some sort of subclass and override the autorotation methods to return portrait only.

From iOS 6 onwards the UI kit has minor changes on how the orientation can be supported in your iOS app.

You will see lot of surprises when you compile a project which was working well on iOS 5 in the latest iOS SDK 6.0. Recently we had to compile lots of client projects to support iOS 6, we were surprised that some of the apps where not working on Landscape orientation. This blog analyses the causes and suggests several fixes.

What are the causes?

1. No `rootViewController` set for the window in the app delegate class

It was a practice earlier that the view controller view would be added as a sub view to the UIWindow object. This is an easy fix:

Change you code from:

[self.window addSubview:aController.view];

to this code:

self.window.rootViewController = aController;

When UIKit receives an orientation notification, it uses the UIApplication object and the root view controller to determine whether the new orientation is allowed. If both objects agree that the new orientation is supported, then the user interface is rotated to the new orientation. Otherwise the device orientation is ignored.

So if in your app the rootViewController is nil, the orientation will be ignored. (For beginners: the root view controller provides the content view of the window.)
As additional information, in case `rootViewController == nil` the console will give the warning:
‘Application windows are expected to have a root view controller at the end of application launch’

2. Deprecation of shouldAutorotateToInterfaceOrientation

For sure everyone is familiar with the code below:

– (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation;

We had to override this method in the view controller sub class and returns the BOOL based on the orientation requirements. Simple and familiar approach, isn’t it? But its deprecated in iOS 6 onwards.

So on iOS 6 onwards, as said already, a view controller that acts as the root view controller of the main window can declare what orientations it supports. It does this by overriding the supportedInterfaceOrientations method. By default, view controllers on devices that use the iPad idiom support all four orientations. On devices that use the iPhone idiom, all interface orientations but upside-down portrait are supported.

– (NSUInteger) supportedInterfaceOrientations
{
   return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft; //supported orientations
}

By returning the supported masks, yo can decide at runtime the required orientations. These are the possible values:

typedef NS_OPTIONS(NSUInteger, UIInterfaceOrientationMask) {

UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait),
UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft),
UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight),
UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown),
UIInterfaceOrientationMaskLandscape = (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
UIInterfaceOrientationMaskAll = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown),
UIInterfaceOrientationMaskAllButUpsideDown = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
};

Also you need to make sure another method shouldAutorotate of UIViewController returns YES as shown below:

– (BOOL)shouldAutorotate{
return YES;
}

This returns whether the view controller’s contents should auto rotate. Incase if you return it as NO, the ‘supportedInterfaceOrientations’ method will never be called. By default auto rotation is supported, so incase you did not implement this method, still ‘supportedInterfaceOrientations’ gets called.

To conclude: your Root View controller code should look something like this:

– (BOOL)shouldAutorotate{
return YES; //Returns YES if auto rotate required
}

– (NSUInteger) supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft; //supported orientations
}

Note: Prior to iOS 6, we used to decide the supported orientations on each and every UIViewController sub classes, but in iOS 6, its decided by the root view controller.

Two more important additions of api’s in iOS 6:

shouldAutomaticallyForwardRotationMethods:

For any custom code which you require upon rotation, we are doing it in either of the below callbacks of UIViewController class:

willRotateToInterfaceOrientation:duration:
willAnimateRotationToInterfaceOrientation:duration:
didRotateFromInterfaceOrientation:

****Note that these methods will never called incase you override ‘shouldAutomaticallyForwardRotationMethods’ to NO in iOS 6.0.

What this api does? This method returns a Boolean value indicating whether rotation methods are forwarded to child view controllers. This method is called to determine whether to automatically forward rotation-related containment callbacks to child view controllers.

The default implementation returns YES. Subclasses of the UIViewController class that implement containment logic may override this method to control how these methods are forwarded. Hence if you override this method and return NO, you are responsible for forwarding the following methods to child view controllers.

– shouldAutomaticallyForwardAppearanceMethods:

Similar to the above, this method determines if the root view controller automatically forwards the appearance methods to its child view controllers or not. The default implementation returns YES. The following methods are having the impact:

beginAppearanceTransition:animated:
endAppearanceTransition

Some example for you to understand:
In IOS 5:
– (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{

return (interfaceOrientation == UIInterfaceOrientationPortrait);

}
In IOS 6:

-(BOOL)shouldAutorotate{
return YES;
}

-(NSInteger)supportedInterfaceOrientations{

//    UIInterfaceOrientationMaskLandscape;
//    24
//
//    UIInterfaceOrientationMaskLandscapeLeft;
//    16
//
//    UIInterfaceOrientationMaskLandscapeRight;
//    8
//
//    UIInterfaceOrientationMaskPortrait;
//    2

//    return UIInterfaceOrientationMaskPortrait;
//    or
return 2;
}

For Navigation Controller

@implementation UINavigationController (iOS6OrientationIssueFixed)

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

@end

@implementation UINavigationController iOS6OrientationIssueFixed)

-(BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
or
return [self.topViewController shouldAutorotate];
}

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

– (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
or
return [self.topViewController preferredInterfaceOrientationForPresentation];

}

@end

Extra

– (BOOL)shouldAutorotate {
return NO;
}

– (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}

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

Overall I hope this article will help you solving your orientation issues in iOS 6. Please leave a comment if this was useful to you, or for any further questions and clarifications. Keep Coding .. Cheers 🙂

Advertisements

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s