Tab-Bar App Crashes on Device Rotation in iOS6 - iphone

THIS IS NOT A DUPLICATE QUESTION. A final working solution has NOT been provided yet so please do not close this question until we've accepted an answer or found and provided our own 100% working solution. Thanks!
==================================================================
Using Xcode 4.5.1, we have a tab-bar app with 5 tabs in it. Each tab contains a UINavigationController, so the entire App thus needs to be viewed in Portrait mode. There is one exception: an "image-gallery" type view controller that we need to open and be viewed full-screen, and in LANDSCAPE mode.
We were able to do this easily in iOS5 using the following code in that one particular ViewController:
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
But shouldAutorotateToInterfaceOrientation has been deprecated in iOS6 and doesn't work any more.
So, to get this to work in iOS6, we've taken the following steps so far:
1) created a subclass of the UITabBarController (which is our rootViewController)
2) set its supportedInterfaceOrientations and preferredInterfaceOrientationForPresentation to UIInterfaceOrientationMaskPortrait (But note that we are NOT implementing the shouldAutorotate method in it)
3) Set the PROJECT/Target supported orientations to ALL
This ALMOST works perfectly: our "Image Gallery" ViewController does respond to both landscape modes - as it should - but it still initially opens in Portrait - which is bad. We need it to open up right in Landscape - and not ever be able to be displayed in Portrait. Right now it still doing both.
Any idea why its doing that - or how to fix it?

I had this exact same problem with an app I work on and this is how I solved it.
First I created a subclass of UITabBarController called NonRotatingTabBarController with the portrait orientation code
NonRotatingTabBarController.h
#import <UIKit/UIKit.h>
#interface NonRotatingTabBarController : UITabBarController
#end
NonRotatingTabBarController.m
#import "NonRotatingTabBarController.h"
#implementation NonRotatingTabBarController
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
#end
Now when you create your Tab Bar Controller, it needs to be an instance of NonRotatingTabBarController
self.tabBarController = [[NonRotatingTabBarController alloc] init]; // or whatever initialising code you have but make sure it's of type NonRotatingTabBarController
Now in the ONLY view controller which needs to have landscape support, you need to override the rotation methods so it does rotate. In my case, it had to be fixed to landscape
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return (UIInterfaceOrientationLandscapeLeft | UIInterfaceOrientationLandscapeRight);
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
In your project/target settings, you MUST have support enabled for all the interface orientations your app uses otherwise it will crash. Let the code above take care of rotation enabling/disabling.
Hope that helps!

shouldAutorotateToInterfaceOrientation method is deprecated in iOS 6
try to implements these following methods.
-(BOOL)shouldAutomaticallyForwardAppearanceMethods{
// This method is called to determine whether to
// automatically forward appearance-related containment
// callbacks to child view controllers.
}
-(BOOL)shouldAutomaticallyForwardRotationMethods{
// This method is called to determine whether to
// automatically forward rotation-related containment
// callbacks to child view controllers.
}
note : these methods just supported in iOS 6.

Related

iPhone Interface Orientation on iOS8

I have been trying to do this for 2 weeks, and just found out the reason it's not working:
Deprecated APIs
The following APIs are deprecated:
The UIViewController methods and properties for interface orientation. Traits and size classes replace them, as described in Unified Storyboards for Universal Apps.
From what's new in iOS 8
What I need is: a FirstViewController, which is the rootViewController to my navigationController. The FristViewController should be only available in Portrait mode (not ever ever ever displayed in Landscape).
Then there are a few intermediate ViewControllers in the navigation stack (which support both orientations), until I reach a LastViewController, which should be available only in LandscapeRight mode (and never ever ever in portrait, or another mode).
I have been trying with the following CustomNavigationController, but apparently things changed in iOS8, and I can't get it to work:
- (BOOL)shouldAutorotate { // Available in iOS 6.0 and later
return YES; // // May use topViewController's, but in this app it always returns YES
}
- (NSUInteger)supportedInterfaceOrientations { // Available in iOS 6.0 and later
if (self.topViewController != nil)
return [self.topViewController supportedInterfaceOrientations];
else
return [super supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { // Available in iOS 6.0 and later
if (self.topViewController != nil)
return [self.topViewController preferredInterfaceOrientationForPresentation];
else
return [super preferredInterfaceOrientationForPresentation];
}
Thanks!
The problem you are having has nothing whatever to do with iOS 8. Here are some points to note:
You have misunderstood the note about what's deprecated. Only methods with names like willRotate are deprecated, and you are not using them anyway.
Nothing has changed with regard to how supportedInterfaceOrientations works. Make sure you test with beta 4, though, because there was a bug in betas 1-3 that prevented presented view controller orientations from working correctly.
"Then there are a few intermediate ViewControllers in the navigation stack (which support both orientations), until I reach a LastViewController, which should be available only in LandscapeRight"... That is impossible, but not because of iOS 8. What you are describing has been illegal since iOS 6! You cannot have different forced orientations for different view controllers in a navigation stack. Only a presented view controller can force rotation (as I have explained here and in many other answers: https://stackoverflow.com/a/21616025/341994).
Implement - (NSUInteger)supportedInterfaceOrientations appropriately in each of the view controllers in the navigation stack. It does not matter what the UINavigationController's supported orientations are. It ought to respect the supported orientations of its displayed view controller.
Also make sure that the all the necessary orientations are checked in your target's "General -> Deployment Info" configuration section.

Handling autorotation for one view controller in iOS7

I've read many answers on SO but I can't seem to get autorotation working on iOS7.
I only need one view controller to rotate, so I don't want to set rotation settings in my Info.plist.
As I understand Apple's documentation, a single view controller can override global rotations settings (from Info.plist) by simply overriding two methods. Info.plist is set to only allow Portrait, and my view controller implements the following methods:
- (NSUInteger)supportedInterfaceOrientations
{
NSLog(#"%s", __PRETTY_FUNCTION__);
return UIInterfaceOrientationMaskAllButUpsideDown;
}
- (BOOL)shouldAutorotate
{
NSLog(#"%s", __PRETTY_FUNCTION__);
return true;
}
I'm seeing those NSLog statements upon rotation but nothing rotates.
If I do configure Info.plist with the proper rotation settings, my view will rotate, but not if I try and rely on my view controller.
Not sure if it matters, but the view I'm trying to rotate is from a .xib using auto layout.
Also, my ViewController is being presented modally and is contained in a navigation controller. I've tried just presenting the view controller by itself and that doesn't work. I've also tried adding a category to UINavigationController to get it's autorotation directions from it's topViewController.
In my case, I had a new iOS7 app with about 30 view controllers created already. I needed auto rotation on just a single modal view controller. I didn't want to have to update the preexisting view controllers.
I selected the orientations I wanted in the plist:
Then I added a category to my app delegate on UIViewController:
#implementation UIViewController (rotate)
-(BOOL)shouldAutorotate {
return NO;
}
#end
Then in the single modal view controller I WANTED to rotate I added this method:
-(BOOL)shouldAutorotate {
return YES;
}
I also discovered, that if my view controller wasn't a modal VC I would need to add category methods on UINavigationController instead, for all VCs that were subsequent to the root view controller, as part of the navigation stack of view controllers - similar to this: https://stackoverflow.com/a/20283331/396429
Simple but it work very fine. IOS 7.1 and 8
AppDelegate.h
#property () BOOL restrictRotation;
AppDelegate.m
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if(self.restrictRotation)
return UIInterfaceOrientationMaskPortrait;
else
return UIInterfaceOrientationMaskAll;
}
ViewController
-(void) restrictRotation:(BOOL) restriction
{
AppDelegate* appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
appDelegate.restrictRotation = restriction;
}
viewDidLoad
[self restrictRotation:YES]; or NO
You need to set the plist value to all possible values, then limit them as you see fit (in the Navigation Controllers and TabBar Controllers. From the UIViewController class description:
In iOS 6 and later, your app supports the interface orientations
defined in your app’s Info.plist file. A view controller can override
the supportedInterfaceOrientations method to limit the list of
supported orientations. Typically, the system calls this method only
on the root view controller of the window or a view controller
presented to fill the entire screen; child view controllers use the
portion of the window provided for them by their parent view
controller and no longer participate directly in decisions about what
rotations are supported. The intersection of the app’s orientation
mask and the view controller’s orientation mask is used to determine
which orientations a view controller can be rotated into.
I've faced such problem - had only one landscape view in my app.
I've used below code to to handle that.
#import "objc/message.h"
-(void)viewWillAppear:(BOOL)animated{
objc_msgSend([UIDevice currentDevice], #selector(setOrientation:), UIInterfaceOrientationLandscapeLeft);
}
I know this is old but I ended up in a more unique situation where we have 50+ ViewController all over the app that I refused to go through and modify and support the same orientation in all of them but one or 2. Which brings me to my answer. I created a UIViewController category that overrides - (BOOL)shouldAutorotate to always return NO or YES depending on device type etc. (this can be done with supported interface orientations too). Then on the ViewControllers I wanted to support more then just portrait, I swizzled shouldAutorotate to return YES. Then forced the orientation change when the view is dismissed on the parent ViewControllers viewWillAppear method using:
[[UIDevice currentDevice] setValue:#(UIInterfaceOrientationPortrait) forKey:#"orientation"].
When all was said and done, I accomplished everything I wanted on a few ViewControllers with < 30 lines of code using a macro for swizzling. Had I done it by replacing shouldAutorotate and supportedInterfaceOrientations on all of the VC's in the application I would have ~250 extra lines of code. and a lot of grunt work adding it in the first place.

How to allow a particular view controller to support for both landscape and portrait orientation

My application is navigation based application which is supporting iOS 6. Except a view controller all others will support only portrait mode. Particular view controller alone has to support both landscape and portrait orientations.
I searched lot there are tons of questions for this but none of the answer is suitable for me. If some one knows, kindly guide me
I set the orientation as Portait in Project -> Targets -> Summary -> Supported orientation
First you should use methods for the iOS6 presented in UIViewController documentation if you are making your app for iOS6. Orientation method like shouldAutorotateToInterfaceOrientation is deprecated in iOS6, alternate method for iOS6 is shouldAutoRotate. You should only use the old method if your app is supporting also iOS5.
Second If you are using UINavigationcontroller in your application and you need to have different interface orientations then navigationController could mess up the interface orientation in the application. Possible solution (worked for me) is to implement a custom UINavigationController and override the interface orientation methods within that custom UINavigationController class, this will make your viewControllers rotate according to the orientation you set because your controllers are pushed from the UINavigationController. Don't forget to add those methods in your particular viewController also.
CustomNavigationController.h
#interface CustomNavigationController : UINavigationController
#end
CustomNavigationController.m
#implementation CustomNavigationController
//overriding shouldRotate method for working in navController
-(BOOL)shouldAutorotate
{
return [self.topViewController shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations
{
return [self.topViewController supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [self.topViewController preferredInterfaceOrientationForPresentation];
}
I really think that the correct way to do it is to set both landscape and portrait as supported orientations, and not allowing a change in the orientation on the VC you don't want to rotate, by returning NO on the
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
if the orientation that was passed to the method was not supposed to be supported.
As mentioned in the comments, this method can no longer be used. You should use :
-(BOOL) shouldAutorotate
And find out the current orientation inside this function and returning NO if you don't want to rotate.
Don't forget to allow your application different Orientations!
otherwise nothing from above will work
Set it in project target on General under Deployment Info section:

screen rotation handling ios

App comprises 5 pages with mesh like hierarchy i.e each page can be reached from any page. Each page have portrait and landscape orientation support.
So i am not able to handle the rotation when pushing and poping the view controllers from navigation controller.
Can any one suggest me the required approach which can be useful in implementing the above.
thanks.
This is probably a problem with iOS 6. UINavigationController does not automatically call supportedInterfaceOrientations: unless you tell it to.
Check out the answers to this question.
Creating a category seems like the best solution.
#implementation UINavigationController (iOS6OrientationFix)
-(BOOL) shouldAutorotate {
return [self.topViewController shouldAutorotate];
}
-(NSUInteger) supportedInterfaceOrientations {
return [self.topViewController supportedInterfaceOrientations];
}
#end

issue with iOS 6 autorotation

I have a viewcontroller in which I wanted it to be presented only in portrait, so I did the following in iOS 6:
-(BOOL)shouldAutorotate
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
however when I rotate the device, it still turns it to landscape. Any idea where else to check this? I put a break point and it hits supportedInterfaceOrientations, but it still rotates
Do you have a navigation controller? The way that iOS6 determines what can be autorotated has changed. It is correctly asking supportedInterfaceOrientations for your view controller but it is probably asking "shouldAutorotate" to another element in your navigation stack hierarchy and accepting that answer. If your navigationController/tabviewController returns yes to this question then it won't consult with your view controller.
You should also provide the apps supported orientations in the app delegate:
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
return UIInterfaceOrientationMaskPortrait;
}
Make sure you add the root view controller properly (not adding it as a subview), but using the following:
[window setRootViewController:myVC];
Also if your view controller is inside a UINavigationController, you should use this category for the navigationcontroller:
#implementation UINavigationController (autorotate)
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
#end
In iOS 6, only the root view controller of the top most full screen controller is asked about rotation. This includes UINavigationController, this class does not ask it's view controllers, it responds directly. Apple now suggest subclassing UINavigationController to override supportedInterfaceOrientations's output.
Make sure that your project settings and info.plist have only portrait orientation selected as they have a higher priority than the app delegate when checking for supported orientations