I have an Universal App that supports all orientations in iPad and only potrait in iPhone / iPod. My code looks somewhat like this:
#implementation UIViewController ( interfaceOrientationHack )
- (void) shouldAutorotateToInterfaceOrientation: ( UIInterfaceOrientation ) toInterfaceOrientation {
if( iPad ) {
return YES;
} else if( toInterfaceOrientation == UIInterfaceOrientationPotrait ) {
return YES;
} else return NO;
}
#end
From one of my controllers, i launch a navigation controller as modal view controller
[ self presentModalViewController: c animated: YES ];
The issue currently is, the modal controller launches correctly in orientation, But when i change my orientation the the modal controller doesn't change its orientation, all the rest of the controllers behave correctly.
Any help will be hugely appreciated. Thanks in advance.
From UIViewcontroller class reference:"By default, the UIViewController class displays views in portrait mode only. To support additional orientations, you must override the shouldAutorotateToInterfaceOrientation: method and return YES for any orientations your subclass supports. If the autoresizing properties of your views are configured correctly, that may be all you have to do."
Since your modal view inherits from UIViewcontroller, you have to override shouldAutorotateToInterfaceOrientation:in your modal view.
Related
How to stop device Orientation into Landscape Mode in UIViewController With UINavigationBarController + UITabbarController.
Its up to individual view controllers to decide if they'd like to auto-rotate. Container view controllers (like UINavigationController, tab-bar controller or one of your own) typically delegate to their children to decide this. Add/override these methods on your view controller:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return NO;
}
- (BOOL)shouldAutorotate
{
return NO;
}
One is the older style and one is the iOS6+ style.
if you want to competently remove orientations for your app in xcode you can set supported orientations
In my app I have multiple views, some views need to support both portrait and landscape, while other views need to support portrait only. Thus, in the project summary, I have all selected all orientations.
The below code worked to disable landscape mode on a given view controller prior to iOS 6:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
Since shouldAutorotateToInterfaceOrientation was deprecated in iOS6 I've replaced the above with:
-(NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMask.Portrait;
}
This method is correctly called when the view appears (I can set a breakpoint to ensure this), but the interface still rotates to landscape mode regardless of the fact that I'm returning the mask for portrait modes only. What am I doing wrong?
It seems that it's currently impossible to build an app that has different orientation requirements per view. It seems to only adhere to the orientations specified in the project summary.
If your are using a UINavigationController as the root window controller, it will be its shouldAutorotate & supportedInterfaceOrientations which would be called.
Idem if you are using a UITabBarController, and so on.
So the thing to do is to subclass your navigation/tabbar controller and override its shouldAutorotate & supportedInterfaceOrientations methods.
try change this code in AppDelegate.m
// self.window.rootViewController = self.navigationController;
[window setRootViewController:navigationController];
this is the complete answer
shouldAutorotateToInterfaceOrientation not being called in iOS 6
XD
In my case I have UINavigationController and my view controller inside. I had to subclass UINavigationController and, in order to support only Portrait, add this method:
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
}
So in the UINavigationController subclass I need to check which orientation is supported by the current topViewController.
- (NSUInteger)supportedInterfaceOrientations
{
return [[self topViewController] supportedInterfaceOrientations];
}
One thing I've found is if you have an old application that is still doing
[window addSubView:viewcontroller.view]; //This is bad in so may ways but I see it all the time...
You will need to update that to:
[window setRootViewController:viewcontroller]; //since iOS 4
Once you do this the orientation should begin to work again.
The best way for iOS6 specifically is noted in "iOS6 By Tutorials" by the Ray Wenderlich team - http://www.raywenderlich.com/ and is better than subclassing UINavigationController for most cases.
I'm using iOS6 with a storyboard that includes a UINavigationController set as the initial view controller.
//AppDelegate.m - this method is not available pre-iOS6 unfortunately
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
NSUInteger orientations = UIInterfaceOrientationMaskAllButUpsideDown;
if(self.window.rootViewController){
UIViewController *presentedViewController = [[(UINavigationController *)self.window.rootViewController viewControllers] lastObject];
orientations = [presentedViewController supportedInterfaceOrientations];
}
return orientations;
}
//MyViewController.m - return whatever orientations you want to support for each UIViewController
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
As stated by others if you're using a UINavigationController and you want to customize various views you'll want to subclass the UINavigationController and make sure you have these two components:
#implementation CustomNavigationController
// -------------------------------------------------------------------------------
// supportedInterfaceOrientations:
// Overridden to return the supportedInterfaceOrientations of the view controller
// at the top of the navigation stack.
// By default, UIViewController (and thus, UINavigationController) always returns
// UIInterfaceOrientationMaskAllButUpsideDown when the app is run on an iPhone.
// -------------------------------------------------------------------------------
- (NSUInteger)supportedInterfaceOrientations
{
return [self.topViewController supportedInterfaceOrientations];
}
// -------------------------------------------------------------------------------
// shouldAutorotate
// Overridden to return the shouldAutorotate value of the view controller
// at the top of the navigation stack.
// By default, UIViewController (and thus, UINavigationController) always returns
// YES when the app is run on an iPhone.
// -------------------------------------------------------------------------------
- (BOOL)shouldAutorotate
{
return [self.topViewController shouldAutorotate];
}
Then in any view that is a portrait only you would include:
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
And in any view that is everything but upside down:
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
Basically as someone stated above, but in more detail:
Create a new file that is a subclass of UINavigationController
Go to your storyboard and then click on the Navigation Controller, set its class to the one that you just created
In this class(.m file) add the following code so it will remain in portrait mode:
(BOOL)shouldAutorotate
{
return NO;
}
(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
This worked for me
This code worked for me:
-(BOOL)shouldAutorotate {
return YES;
}
-(NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
}
iPhone/iPad App Orientation check out my own answer
The best way I think is to do a Category rather than subclassing UINavigationController or UITabbarController
your UINavigationController+Rotation.h
#import <UIKit/UIKit.h>
#interface UINavigationController (Rotation)
#end
your UINavigationController+Rotation.m
#import "UINavigationController+Rotation.h"
#implementation UINavigationController (Rotation)
-(BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
#end
Try to make all your controller import this category and this work like a charm.
You can even make a controller not rotating and pushing another controller that will rotate.
Try add shouldAutorotate method
Firstly in order to make your app work in only mode you should be returning UIInterfaceOrientationMaskLandscape. In case you want to keep only portrait mode, you are doing things correctly.
Just add the UISupportedInterfaceOrientations key in the Info.plist and assign the interface orientation values your app intends to keep.
Also, you should be returning false from shouldAutoRotate in case you want to avoid auto rotation totally. But I would suggest you to return true from here and specify the correct orientations in supportedInterfaceOrientations method.
I have the same situation as you. I know you already accepted an answer, but I thought I'd add another one anyway. This is the way I understand the new version of the rotation system to work. The root view controller is the only view controller to ever be called. The reasoning, I believe, is that with child view controllers it doesn't make sense often to rotate their views since they will just stay within the frame of the root view controller anyway.
So, what happens. First shouldAutorotate is called on the root view controller. If NO is returned then everything stops. If YES is returned then the supportedInterfaceOrientations method is invoked. If the interface orientation is confirmed in this method and the global supported orientations from either the Info.plist or the application delegate, then the view will rotate. Before the rotation the shouldAutomaticallyForwardRotationMethods method is queried. If YES (the default), then all children will receive the will and didRotateTo... methods as well as the parent (and they in turn will forward it to their children).
My solution (until there is a more eloquent one) is to query the last child view controller during the supportedInterfaceOrientations method and return its value. This lets me rotate some areas while keeping others portrait only. I realize it is fragile, but I don't see another way that doesn't involve complicating things with event calls, callbacks, etc.
If you are using UINavigationController, you have to implement shouldAutorotate and supportedInterfaceOrientations in subclass of UINavigationController.
These are able to control by two steps, if shouldAutorotate returns YES then effective supportedInterfaceOrientations. It's a very nice combination.
This example, my mostly views are Portrait except CoverFlowView and PreviewView.
The CoverFlowView transfer to PreviewView, PreviewView wants to follow CoverFlowCView's rotation.
#implementation MyNavigationController
-(BOOL)shouldAutorotate
{
if ([[self.viewControllers lastObject] isKindOfClass:NSClassFromString(#"PreviewView")])
return NO;
else
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
if ([[self.viewControllers lastObject] isKindOfClass:NSClassFromString(#"CoverFlowView")])
return UIInterfaceOrientationMaskAllButUpsideDown;
else
return UIInterfaceOrientationMaskPortrait;
}
...
#end
my solution : subclassed UINavigationController and set it as window.rootViewController
the top viewcontroller of the hierarchy will take control of the orientation , some code examples : subclassed UINavigationController
The answers here pointed me in the correct direction although I couldn't get it to work by just cut and pasting because I am using UINavigationControllers inside of a UITabBarController. So my version in AppDelegate.m looks something like this, which will work for UITabBarControllers, UINavigationControllers or UINavigationControllers within a UITabBarController. If you are using other custom containment controllers, you would need to add them here (which is kind of a bummer).
- (UIViewController*)terminalViewController:(UIViewController*)viewController
{
if ([viewController isKindOfClass:[UITabBarController class]])
{
viewController = [(UITabBarController*)viewController selectedViewController];
viewController = [self terminalViewController:viewController];
}
else if ([viewController isKindOfClass:[UINavigationController class]])
{
viewController = [[(UINavigationController*)viewController viewControllers] lastObject];
}
return viewController;
}
- (NSUInteger)application:(UIApplication *)application
supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
NSUInteger orientations = UIInterfaceOrientationMaskPortrait;
UIViewController* viewController = [self terminalViewController:window.rootViewController];
if (viewController)
orientations = [viewController supportedInterfaceOrientations];
return orientations;
}
Another key thing to note is that you must override supportedInterfaceOrientations in your UIViewController subclasses or it will default to what you specified in your Info.plist.
I'm having issue with auto rotating in my view which is inside a UINavitionViewController and the navigationViewcontroller is inside a tabBarViewController.
I subclassed tabBarViewController. The problem is the interfaceorientation works fine on the first view inside the tabViewController, but whenever I push to another view it doesn't work.
This is a code in subclass tabBarController
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
if([self.selectedViewController isKindOfClass:[UINavigationController class]]){
return [[(UINavigationController*)self.selectedViewController visibleViewController] shouldAutorotateToInterfaceOrientation:interfaceOrientation];
} else {
return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
}
You should have a UIViewController inside a UINavigationController inside a UITabBarController. The rotation is decided by shouldAutorotateToInterfaceOrientation: in your UIViewController. You need to override that method for every UIViewController to return the desired value, i.e., YES if you want it to rotate and NO if you don't want it to.
You shouldn't override shouldAutorotateToInterfaceOrientation: in UINavigationController or UITabBarController.
You have to override shouldAutorotateToInterfaceOrientation: in the view controllers for all the views in the tab bar.
If a view controller's shouldAutorotateToInterfaceOrientation: returns NO, then the tab bar will not rotate, even if the view is hidden at the time of the rotation.
I'm new to StackOverflow, and also new to objective-c.
I have tried looking for a couple weeks now to find something that gives me a hint of what to do, but I can't seem to get through this one without asking a question. It feels like it should be simple, but...
I'm having a problem with the rotation of a "rootView" controller. It should show up in landscape (which it did, before I decided to use a navigation controller). The simulator shows up in the correct orientation, but it has loaded the view rotated 90 degress left, so that the text reads from bottom to top, sideways, so you have to cock your head left. The leftmost portion of the view is visible, but the remainder of the view runs off the top of the screen. It's a large program (I'm finding that, in general, with objective-c, but enjoying it nonetheless...), but here's the gist of what I think are the important code areas:
In the appDelegate, I create a window, my rootViewcontroller, and the navcontroller:
// .h file:
UIWindow *window;
wordHelper3ViewController *viewController;
UINavigationController *navigationController;
Then in the implementation:
//.m file
- (BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Hide status bar
application.statusBarHidden = YES;
// --- Add the view controller's view to the window and display.
[window addSubview:viewController.view];
[window addSubview:navigationController.view];
[window makeKeyAndVisible];
return YES;
}
Then I release all of these in the dealloc.
In my root view, I'm doing this:
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
if (interfaceOrientation == UIInterfaceOrientationLandscapeRight){
return YES;
} else {
return NO;
}
}
I implemented the following, to see what was happening, and I'm getting the log message on startup immediately:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
if (fromInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || fromInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {
NSLog(#"didRotate...");
}
}
Other than this, I don't think I'm doing anything in my code that should affect the view(s). So now, on to InterfaceBuilder:
In the attributes inspector for both the rootView and the navigation controller, the orientation is set to landscape. For the rootView, under referencing outlets, the “view” is set to file's owner. I also have an action attached to the view (touchUpInside) – I changed its class to UIButton, because I wanted to resignFirstResponder when the user clicks anywhere in the background.
For the navigationController, under Referencing outlets, it shows a connection from the navigationController to my appDelegate.
In the main window xib, the nav contoller shows up in the list, and the view controller shows up as a subview of it.
The viewcontroller also shows up on its own, in the main window's list of objects.
The only thing that stands out to me, is that when I double click the window object, IT comes up visually in portrait mode.
Does anyone have any ideas?
If you want the startup orientation to be different from the initial Portrait mode, you need to edit your info.plist file. Add an entry for "intial interface orientation" and set the value that you want.
Here's a screenshot:
If you want to rotate a UINavigationController, it will take a bit more work. See this similar question for more information.
I think your problem is that you are adding your navigation controller as a subview to the window and orientation notifications are only sent to the first subview in "window".
Try this instead.
//.m file
- (BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Hide status bar
application.statusBarHidden = YES;
// --- Add the view controller's view to the window and display.
[window addSubview:viewController.view];
[viewController.view addSubview:navigationController.view];
[window makeKeyAndVisible];
return YES;
}
Its annoying, but I've resolved orientation problems by only every having one subview in "window" and then controlling everything through that one subview (in this case viewController)
Forgive me if I'm misunderstanding, but shouldn't this
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
if (interfaceOrientation == UIInterfaceOrientationLandscapeRight){
return YES;
} else {
return NO;
}
}
really be this
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
?
I have a UITabBarController, and each tab handles a different UIViewController that pushes on the stack new controllers as needed. In two of these tabs I need, when a specific controller is reached, the ability to rotate the iPhone and visualize a view in landscape mode. After struggling a lot I have found that it is mandatory subclassing UITabBarController to override shouldAutorotateToInterfaceOrientation. However, if i simply return YES in the implementation, the following undesirable side effect arises:
every controller in every tab is automatically put in landscape mode when rotating the iPhone.
Even overriding shouldAutorotateToInterfaceOrientation in each controller to return NO does not work: when the iPhone is rotated, the controller is put in landscape mode.
I implemented shouldAutorotateToInterfaceOrientation as follows in the subclassed UITabBarController:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if([self selectedIndex] == 0 || [self selectedIndex] == 3)
return YES;
return NO;
}
So that only the two tabs I am interested in actually get support for landscape mode.
Is there a way to support landscape mode for a specific controller on the stack of a particular tab?
I tried, without success, something like
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if([self selectedIndex] == 0 || [self selectedIndex] == 3)
{
if ([[self selectedViewController] isKindOfClass: [landscapeModeViewController class]])
return YES;
}
return NO;
}
Also, I tried using the delegate method didSelectViewController, without success.
Any help is greatly appreciated.
Thank you.
Here's an extension to UITabBarController that delegates calls to shouldAutorotateToInterfaceOrientation to the currently selected child controller. Using this extension, you don't need to subclass UITabBarController anymore and you can use shouldAutorotateToInterfaceOrientation in your controllers like one would expect.
UITabBarController+Autorotate.h:
#import <UIKit/UIKit.h>
#interface UITabBarController (Autorotate)
#end
UITabBarController+Autorotate.m:
#import "UITabBarController+Autorotate.h"
#implementation UITabBarController (Autorotate)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
UIViewController *controller = self.selectedViewController;
if ([controller isKindOfClass:[UINavigationController class]])
controller = [(UINavigationController *)controller visibleViewController];
return [controller shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
#end
This worked for me:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if(self.selectedIndex == 0 && [[[self.viewControllers objectAtIndex:0] visibleViewController] isKindOfClass:[MyViewController class]])
return YES;
else
return NO;
}
I've been able to use this for a while now (from my app's tab bar controller) without problems:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
That way, in the appropriate VC, we get to do the real check, in this case for a photo gallery view (what else?):
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
My gallery view isn't even at the top-of-stack for a given Nav Controller. It still gets called.
Alas, I just discovered that this doesn't work so well when the VC is lurking within the MoreViewController (as opposed to the four main tabs). In that case, my gallery VC never gets called. I think it's because the VC I've been calling all along is really the nav controller from the selected tab, which then propagates things to the appropriate VC, in this case my photo gallery VC. But for the More VC, things don't work so nicely ... aaaand things go rotationally downhill from there. :\
I tried using the modifications by Andreas (see elsewhere in this thread), to no avail. Clues welcome!
I ran into the same issues as you did when working with the UITabBarController. I needed to control which UIViewControllers were allowed to rotate and which were not. My main problem was with the MORE tab. I did not want any of the UIViewControllers included in the MORE tab to rotate.
My solution was to create my own UITabBarController which I called MyTabBarController:
#interface MyTabBarController : UITabBarController <UITabBarDelegate> {
}
Then I implemented the shouldAutorotateToInterfaceOrientation method:
#implementation MyTabBarController
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
UIViewController *controller = [self selectedViewController];
if ((controller == [self moreNavigationController]) || ([self selectedIndex] == 4))
{
return interfaceOrientation == UIInterfaceOrientationPortrait;
}
return [controller shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
#end
I needed to discover if the MORE tab was selected. This is a two step process; when the MORE tab is selected initially the API returns a selectedIndex higher than 4 so I needed to compare the selected controller with the moreNavigationController.
If an UIViewController is selected from within the MORE tab then the selectedIndex is finally 4 but the selectedController is not the moreNavigationController anymore but the UIViewController selected.
The if ((controller == [self moreNavigationController]) || ([self selectedIndex] == 4)) takes care of this issue.
Now, when I run my application my UIViewControllers in the MORE tab are not rotated. I hope this will help other developers who are running into the same issues as I did.
Emilio
From what I have seen here and elsewhere I have stitched together a solution that uses the method shouldAutorotate since the old shouldAutorotateToInterfaceOrientation has been deprecated.
I have placed it inside a category to UITabBarController. I so hope this is admissible!
// call to method shouldAutorotate replaces call to method shouldAutorotateToInterfaceOrientation (deprecated)
-(BOOL)shouldAutorotate
{ // check whether selected view controller should autorotate
UIViewController *controller = self.selectedViewController;
if ([controller isKindOfClass:[UINavigationController class]])
{ // in case it is a navigation controller: get visible view of that
controller = [(UINavigationController *)controller visibleViewController];
}
return [controller shouldAutorotate];
}
Thank you, Thank you, Thank you. This has been 2 days in figuring out how to do this. Here is my take on all of your great help when you have a tabBarController with navigationControllers.
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
UIViewController *controller = self.selectedViewController;
if ([controller isKindOfClass:[UINavigationController class]])
controller = [(UINavigationController *)controller visibleViewController];
if([controller isKindOfClass:[LOCviewcontroller class]])
return YES;
else
if([controller isKindOfClass:[personWebSiteView class]])
return YES;
else return NO;
}
Any critique of a neophite coder's code is always appreciated...jack
Is it really OK to subclass UITabBarController (as suggested in the accepted answer above)?
I've understood that Apples says something like "you should never ever subclass UITabBarController or UINavigationController" - or have I misunderstood?
Anyway; I found this tutorial where they subclasses a UIViewController in which they put a UITabBarController.