I'm building an app for iOS5 and iOS6. I have this UIViewController inside a UINavigationController and I want it to stay in portrait mode.
The code works for iOS5, but not in iOS6.
// iOS5 rotation
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if (interfaceOrientation == UIInterfaceOrientationPortrait)
return YES;
else
return NO;
}
// iOS6 rotation
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
What's the problem here??? On SO I've found lot's of similar questions, but generally the answers are not working for me.
EDIT:
Maybe I was not precise, but I need a SINGLE view controller (the homepage of my app) to stay in portrait mode and not all the app.
First of all, A lot depends on with which controller is your UIViewController embedded in.
Eg, If its inside UINavigationController, then you might need to subclass that UINavigationController to override orientation methods like this.
subclassed UINavigationController (the top viewcontroller of the hierarchy will take control of the orientation.) needs to be set it as self.window.rootViewController.
- (BOOL)shouldAutorotate
{
return self.topViewController.shouldAutorotate;
}
- (NSUInteger)supportedInterfaceOrientations
{
return self.topViewController.supportedInterfaceOrientations;
}
From iOS 6, it is given that UINavigationController won't ask its UIVIewControllers for orientation support. Hence we would need to subclass it.
MOREOVER
Then, For UIViewControllers, in which you need only PORTRAIT mode, write these functions
- (BOOL)shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
return (UIInterfaceOrientationMaskPortrait);
}
For UIViewControllers, which require LANDSCAPE too, change masking to All.
- (NSUInteger)supportedInterfaceOrientations
{
return (UIInterfaceOrientationMaskAllButUpsideDown);
//OR return (UIInterfaceOrientationMaskAll);
}
Now, if you want to do some changes when Orientation changes, then use this function.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
}
VERY IMPORTANT
in AppDelegate, write this. THIS IS VERY IMP.
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return (UIInterfaceOrientationMaskAll);
}
If you want to provide only Portrait mode for all your viewcontrollers, then apply the portait mask. i.e UIInterfaceOrientationMaskPortrait
Otherwise, if you want that some UIViewControllers stay in Portrait while others support all orientations, then apply an ALL Mask. i.e UIInterfaceOrientationMaskAll
Try out this :
-(BOOL)shouldAutorotate
{
return NO;
}
-(NSUInteger)supportedInterfaceOrientations
{
UIInterfaceOrientationMask orientationMask = UIInterfaceOrientationMaskPortrait;
return orientationMask;
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
This should work in order to support the portrait mode only.
Related
Right I have done a lot of research on this and have hit a block.
I am developing an app which has multiple view controllers. I have the 'homepage', if you will, which all the others views can be access from (this ViewController.m). I also then have a UINavigationController.m attached to the ViewController.m to allow a full navigation bar etc.
My problem is that I am wanting all views to be shown in portrait EXCEPT one view, which I only want in landscape. In my app options, I have set it to enable all orientations and then I was reading that you should override the main orientation in your root view controller (which I assume is the navigation controller?), so I add:
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
This overrides everything and only portrait works. I have then tried adding:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskLandscape;
}
To the view that I want to have in landscape only but it fails to work in the simulator.
Can anyone provide me with a solution? :/
(I would like iOS5 and iOS6 support if possible)
[EDIT]
I should add, I haven't edited AppDelegate in any way.
ADDED:
You can access this project on github
ios6rotations
Sorry guys for asking the question about screen rotation in iOS 6 but this is really a pain in the ass..and I still can't understand it completely - for some reason it behaves differently under certain circumstances.
I have the following simple hierarchy of views in my test app:
What I'm trying to achieve is - to keep blue controller in landscape only and red one is only in portrait.
I have a subclass of UINavigationController with such code inside:
#implementation CustomNavController
- (BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}
- (NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
#end
In my blue controller I implemented this:
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}
And in red controller this:
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
Now I have the following behavior:
App started in landscape (OK)
When I press the button my red controller pushed in landscape too (this is not ok because it must be shown in Portrait)
It successfully rotates to portrait but not backward to landscape
If I leave the red controller in Portrait mode my blue controller (which is restricted to landscape) shows in Portrait mode.
P.S.
All my rotation methods(posted above) are getting called normally.(by the way why do these methods getting called so many times per screen transition - 5-6 times)
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation does not getting called with pushing
All(except portraitUpsideDown) orientations are included in plist.
The question is - how to force rotation to supported orientation in each controller?
I suggest you to post here (as answers) any 100% working code to handle rotations in ios6 (for example if you have some for iPad with SplitController) - I'll keep this question in favorites to have all in one place when I need to handle some specific situations. Cheers!
ADDED:
Please do not post this as answer from landscape to portrait I hope that there'
s more elegant way to do it.
Using -[UIDevice setOrientation:] is a private API, and will get your application rejected. See this question.
What you ask is not possible using public API and is also not recommended from HIG standpoint. What is supported and you should implement, is modal presentation of the different view controllers with different supported interface orientation. This is why the default implementation of UINavigationController is to always rotate; it assumes all view controllers have the same supported interface orientations.
Take for example video playback on iPhone. Open the video apps (that comes with iOS). The root view controller only supports portrait orientation. However, start a video, and a modal view controller pops up which only supports landscape interface orientations. This seems exactly the behavior you wish to achieve.
This is why preferredInterfaceOrientationForPresentation is not called. preferredInterfaceOrientationForPresentation only gets called when using presentViewController:animated:.
A small gotcha, if you require a navigation bar in each stage of your scene, you will need to enclose each modal view controller with a navigation controller. You can then pass the required data in prepareForSegue: by accessing topViewController of the navigation controller object in the segue.
Here is an example project which behaves correctly according to your requirements (or at least will give you ideas how to implement):
http://www.mediafire.com/?zw3qesn8w4v66hy
My two cents worth.
You can present an empty transparent modal view quickly then dismiss it, maybe on ViewDidLoad: or viewWillAppear: on your ViewController and ViewControllerSecond class as a quick workaround.
Also, in storyboard, you can set ViewController class orientation to landscape visually.
use this line for programmatically change orientation... work 100%
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
and also when you add this line at that time one warning appear and for remove this warning just add bellow code on you implementation file.. at the top.
#interface UIDevice (MyPrivateNameThatAppleWouldNeverUseGoesHere)
- (void) setOrientation:(UIInterfaceOrientation)orientation;
#end
and after that in bellow method just write this code if required..
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return your supported orientations
if (currentMainView==blueOne) {
return toInterfaceOrientation== UIInterfaceOrientationPortrait;
}
}
I have a similar situation in one of my apps (although do note that I am not using UINavigationController).
Change the shouldAutorotate methods in both of your viewControllers:
//in blue (landscape only)
-(BOOL)shouldAutorotate{
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) {
return YES;
}
else {
return NO;
}
}
//in red (portrait only)
-(BOOL)shouldAutorotate{
if (self.interfaceOrientation == UIInterfaceOrientationPortrait) {
//note that UIInterfaceOrientationIsPortrait(self.interfaceOrientation) will return yes for UIInterfaceOrientationPortraitUpsideDown
return YES;
}
else {
return NO;
}
}
Keep the supportedInterfaceOrientations methods the same.
#pragma mark- Orientation Delegate Method:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{ Orientation = [UIApplication sharedApplication].statusBarOrientation;
if (Orientation == UIInterfaceOrientationLandscapeLeft || Orientation == UIInterfaceOrientationLandscapeRight)
{
// self.scrollView.contentOffset = CGPointMake(self.view.bounds.size.width,1200);
[scrollView setScrollEnabled:YES];
[scrollView setContentSize:CGSizeMake(768, 2150)];
}else if (Orientation == UIInterfaceOrientationPortrait || Orientation == UIInterfaceOrientationPortraitUpsideDown)
{
[scrollView setScrollEnabled:YES];
[scrollView setContentSize:CGSizeMake(768, 1750)];
}
}
In order to use navigation with orientation together, you should take a bunch of viewcontrollers like an array.
After that checkout following methods,
-(BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
this changes in your methods will help you a lot.
Enjoy Programming!
I have UITableViewController in which on one tab is UINavigationViewController. UINavigationController root view controller is UITableViewController, and when clicked on cell, UIViewController appears which has to be locked in Landscape.
I want every Controller to be locked in Portrait, except the mentioned UIViewController that must be locked in Portrait.
I have tried the following:
CustomTabBarController.m:
#import "CustomTabBarController.h"
#implementation CustomTabBarController
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// You do not need this method if you are not supporting earlier iOS Versions
return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
- (BOOL)shouldAutorotate{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations{
return [self.selectedViewController supportedInterfaceOrientations];
}
#end
CustomNavigationController.h:
#import "CustomNavigationController.h"
#implementation CustomNavigationController
-(NSUInteger)supportedInterfaceOrientations
{
return [self.topViewController supportedInterfaceOrientations];
}
-(BOOL)shouldAutorotate
{
return YES;
}
#end
And in UIViewController that must be locked in to Landscape, I have put:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return ((interfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (interfaceOrientation == UIInterfaceOrientationLandscapeRight));
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
-(BOOL)shouldAutorotate
{
return YES;
}
But it doesn't work, I can rotate it to Landscape and it will stay locked in Landscape, but I want it to appear automatically in Landscape.
Any suggestions?
I had a big problem in the past with UITabBarController not respecting my supported interface orientations of displayed view controllers.
I solved the problem by sub-classing UITabBarController and capturing whenever an item was selected. I'd then call down to the view controller myself, ask it what the supported orientations are and force a rotation myself if needed. I would also call down to the selected view controller on rotations to set/change my supported orientations.
I implemented the UITabBarDelegate and used didSelectItem to capture tab switches. I'm not sure if there is a better way to do it now.
Try to override method
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
Try with the method to block some orientations for a particular window:
– application:supportedInterfaceOrientationsForWindow:
I've got an app where I have a UINavigationController subclass as my rootViewController. I've got a UITableViewController that lets the user edit some settings, it should always be in portrait mode. My app also needs to support all other orientations after I push a MoviePlayer component onto the navigation controller.
The UITableViewController subclass has this implementation of supportedInterfaceOrientations:
- (NSUInteger)supportedInterfaceOrientations {
LLog();
return UIInterfaceOrientationMaskPortrait;
}
The logging command tells me that this gets actually called.
The problem is that the return value is not respected, i.e. the screen turns to landscape orientation when I turn the device.
What can I do to make the settings view always show in portrait but allow orientation changes for the video viewer?
More information: my UINavigationController subclass doesn't override shouldAutorotate or supportedInterfaceOrientations. I haven't implemented
- (NSUInteger)application:(UIApplication *)application
supportedInterfaceOrientationsForWindow:(UIWindow *)window
method in my AppDelegate and I have enabled all orientations in the target summary.
I had issue that some ViewControllers in the navigation stack support all the orientations, some only portrait, but UINavigation controller was returning all app supported orientations, this little hack helped me.
#implementation UINavigationController (iOS6OrientationFix)
-(NSUInteger) supportedInterfaceOrientations {
return [self.topViewController supportedInterfaceOrientations];
}
#end
You also need to add:
- (BOOL)shouldAutorotate {
return NO;
}
and set the supported rotations for the root view controller in the app plist file to only portrait.
Category for UINavigationController not working for me. I don't know why. I solve my problem with such category of UIViewController:
#implementation UIViewController (Orientation)
- (BOOL) shouldAutorotate
{
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
NSUInteger orientations = UIInterfaceOrientationMaskPortrait;
if ([self isKindOfClass:[PlayerViewController class]])
{
orientations |= UIInterfaceOrientationMaskLandscapeLeft;
orientations |= UIInterfaceOrientationMaskLandscapeRight;
}
return orientations;
}
#end
I know you have to use the new rotation methods for IOS6, but it seems the method I've written doesn't work.
I setted my plist file to allow all rotation but not portraitUpsideDown
I then had the following in my appDelegate:
self.navController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
[self.window setRootViewController:navController]; //add nav controller to be the root view
Then in my rootView, to push to another controller, I have:
WebViewViewController *webController = [[JBWebViewViewController alloc] init];
webController.urlString = urlName;
[self.navigationController pushViewController:webController animated:YES];
And In the web controller I have:
#pragma mark - System Rotation Methods
//for any version before 6.0
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
//only allow landscape
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
/for 6.0+
- (BOOL)shouldAutorotate{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
What I want do, is to allow 3 rotations in the root view, but when switch to the web view(note I do push navigation, not add subview), I only want to allow portrait view.
Someone help me please
-------UPDATE----------
I've created my own navController subclass of UINavigationController, I have an BOOL landscapeModeOn that I can setup to tell auto rotation specs
#pragma mark - System Rotation Methods
//for any version before 6.0
- (BOOL)shouldAutorotateToInterfaceOrientation (UIInterfaceOrientation)interfaceOrientation
{
if (landscapeModeOn) {
return interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown;
} else {
return interfaceOrientation == UIInterfaceOrientationPortrait;
}
}
//for 6.0+
- (NSUInteger)supportedInterfaceOrientations{
if (landscapeModeOn) {
return UIInterfaceOrientationMaskAllButUpsideDown;
} else {
return UIInterfaceOrientationMaskPortrait;
}
}
- (BOOL)shouldAutorotate{
UIInterfaceOrientation ori = [UIDevice currentDevice].orientation;
if (landscapeModeOn) {
return ori != UIInterfaceOrientationPortraitUpsideDown;
} else {
return ori == UIInterfaceOrientationPortrait;
}
}
IN the subviews loading, I do:
- (void)viewWillAppear:(BOOL)animated{
//get nav controller and turn off landscape mode
JBNavController *navController = (JBNavController*)self.navigationController;
[navController setLandscapeModeOn:NO];
[navController shouldAutorotate];
}
--------------------Refer to best answer's quote
For IOS6, apple is now focusing on using the Storyboard's AutoLayout together with the new rotation definitions, it is difficult to fix some tiny bugs for IOS6 based on the ios 4.3 and ios 5 coding structure
From applefreak, his suggestion hinted on:
A main challenge in your case is not handling the orientations. Actually it's locking the different view controllers to particular orientation
Although manual rotate view seems really hard to do without any bugs, but it seems the only solution I am now trying, will post more once solved
For your situation you will have to subclass your NavigationController and add the shouldAutorotate and supportedInterfaceOrientations methods to it. iOS6 now asks your navigation stack in the reverse order to iOS5 so it will ask your NavigationController first and if that returns YES it won't even consult with it's child view controllers. To fix that you have to add the logic yourself to do this
So in your subclassed navigation controller you manually ask your current viewcontroller it's autorotation abilities:
- (BOOL)shouldAutorotate
{
return self.topViewController.shouldAutorotate;
}
- (NSUInteger)supportedInterfaceOrientations
{
return self.topViewController.supportedInterfaceOrientations;
}
and in your individual viewcontrollers you can now implement those functions and have them return the values you want which you have defined in your question.
I hope this makes sense.
Following code is wrong!
- (BOOL)shouldAutorotate{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
Remember that supportedInterfaceOrientations gets called only if shouldAutoRotate returns YES. Now root view controllers decides whether it's children rotates or not.
In your case I would suggest to have a base class controller to your self.viewController and set self.viewController to root view controller not navigationController otherwise rotation methods won't be invoked! I ran into this same issue. You should have a HAS-A relationship with base view controller and it's children. Return Yes/No from ShouldAutoRotate based on active children and same for supported orientation. If you follow this architecture then it would be consistent for complex App.
For example in your case BaseviewController should return YES from shouldAutoRotate and returns UIInterfaceOrientationPortrait from supported orientation delegate when webviewController is active. I hope this makes sense.
It common code for iOS5 and iOS6
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
{
// here to implement landscope code
}
else
{
// here to implement setframePortrait
}
}