I have th e following code in my uiviewcontroller.m file:
-(BOOL)shouldAutoRotate
{
return YES or NO;
}
I have tried both YES and NO but my view controller still rotates? I am using iOS 7 and the other iOS solutions I found aren't working for me either.
It probably happens because your controller instantiated as child of UINavigationController in view hierarchy. The UINavigationController does not query child controllers if they want to be rotated or not.
I had the same issue; I wanted to disable autorotation, so all hierarchy of particular UINavigationController is locked in Portrait. I ended with this class:
#implementation FixedOrientationNavigationController
- (BOOL)shouldAutorotate {
return NO;
}
#end
which I put instead of UINavigationControllr class in Storyboard for hierarchies which I need to lock Portrait. Just this, I do not need to implement shouldAutorotate in each controller.
You may also check this link: Orientation Respectful UINavigationController, it tries to implement "orientation respectful" UINavigationController.
It works, but in some cases it leads to weird results, for example, when user rotate to Landscape and then go back to the controller which should only support Portrait.
You can also set the orientation by clicking on project name and then general ,here you can set the orientations you want and set
- (BOOL)shouldAutorotate {
return NO;
}
Hope you got.
Related
I am developing an application which is in portrait mode.
But I want one view controller should display in landscape as well as in portrait mode.
I tried the following code but it doesn't work (not called).
- (BOOL) shouldAutorotate
{
return NO;
}
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return UIInterfaceOrientationMaskPortrait;
}
First, you need to set in your plist all the orientations your app supports, this can be done in the 'General' tab in the project under "Deployment Info", for example:
Then, you can use the method supportedInterfaceOrientations,
I assume you are presenting the view controller modally, so simply override it, on the presenting viewController, which need to be only in portrait use:
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
and in your presented viewController, which should also supports landscape, use: (or whatever orientation mask you would like)
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
P.S - there is a different behavior for viewController that is presented modally and for a viewController that push in a navigationController stack:
modalViewController will call its own supportedInterfaceOrientations, and will support these orientations
pushedViewController will call its navigationController supportedInterfaceOrientations, and will support these orientation.
So, if you are presenting the viewController modally, you need to override its own supportedInterfaceOrientations, but if you push this viewController, you need to set some BOOL property in the navigationController, so it will know which orientations to supports.
I advise you to present this viewController modally, it's more natural to use modalViewController for different device orientations.
P.S #2: about shouldAutorotate: if it returns 'NO', than supportedInterfaceOrientations is not called, so return 'YES'. It only says, if to rotate automatically when the device rotates. if it returns 'NO', you need to explicit rotate the viewController.
Well I hope I helped and didn't write an answer that is completely not regarded to what you asked... :)
When presenting a modal with UIModalPresentationCustom, it ignores the orientation methods, and displays / rotates to whatever the presenting VC is configured to.
Example:
Presenting VC supports Landscape and Portrait.
Presented VC supports Portrait only (via preferredInterfaceOrientationForPresentation and supportedInterfaceOrientations.
When presenting it in landscape without UIModalPresentationCustom, it rotates the view back to portrait, then presents the VC accordingly. Unfortunately, because I need the presenting VC to stay visible below, I am forced to use UIModalPresentationCustom. And when that happens, the presenting VC is forced into landscape mode, creating a messed up UI and generating constraint issues. And even when presenting in portrait, it becomes allowed to rotate into landscape, ignoring that shouldAutorotate returns NO.
PS: I found a workaround on iOS 7 by adding this method to my App Delegate, but it doesn't fix it on iOS 8.
#implementation UIViewController (customModalFix)
- (BOOL)shouldAutorotate
{
if ([self.presentedViewController isKindOfClass:[IntroViewController class]]) {
return [self.presentedViewController shouldAutorotate];
}
return YES;
}
#end
EDIT: Implementing supportedInterfaceOrientations on the presenting VC doesn't help at all, since it is only called when the view is loaded, not when a VC is about to be presented over it. Still haven't found a solution to this problem.
Maybe I'm late. The point is, when using UIModalPresentationCustom, the presenting VC will not disappear, and the presented VC is not considered to be presented full-screen (even if it does take up the full screen). Thus, it's the presenting VC that is consulted for the supported interface orientations. So the solution can be like:
- (NSUInteger)supportedInterfaceOrientations
{
if (self.presentedViewController) {
return [self.presentedViewController supportedInterfaceOrientations];
}
return [super supportedInterfaceOrientations];
}
If you only use UIModalPresentationCustom to keep the presenting VC visible below, say you need a clear colored VC, my answer here may work for you too:
https://stackoverflow.com/a/29167837/46940801
My app can autorotate but I need one of the views to only show in portrait mode and don't know how to achieve this.
I tried this (among other things) but the view in question still rotates:
// ViewController.m
-(BOOL)shouldAutorotate
{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
Can someone kindly point out what I'm doing wrong? Thanks.
-edit-
It's for iOS 6.1
When a UINavigationController is involved, create a category on the UINavigationController and override supportedInterfaceOrientations.
#import "UINavigationController+Orientation.h"
#implementation UINavigationController (Orientation)
- (NSUInteger)supportedInterfaceOrientations {
return [self.topViewController supportedInterfaceOrientations];
}
- (BOOL)shouldAutorotate {
return YES;
}
#end
Now, iOS containers (such as UINavigationController) do not consult their children to determine whether they should autorotate.
How to create a category
1. Add a new file (Objective c- category under cocoa touch)
2. Category : Orientation on UINavigationController
3. Add the above code to UINavigationController+Orientation.m
Swift 3 version the accepted answer:
extension UINavigationController {
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
// Change `.portrait` to whatever your default is throughout your app
return topViewController?.supportedInterfaceOrientations ?? .portrait
}
open override var shouldAutorotate: Bool {
return true
}
}
As per the documentation.
A view controller can override the supportedInterfaceOrientations method to limit the list of supported orientations.
So we need to override shouldAutorotate and supportedInterfaceOrientation to target view controllers.
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.
This will work if you have very simple configuration like your target view controller is the rootViewController of window or being presented covering whole screen.
In case when configuration of target view controller is complex like embedded in some other container view controllers.
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.
So may be default implementation of these container view controllers not asking there children for there supportedInterfaceOrientation preference.
So to allow our target child view controller to specify there supportedIntefaceOrientation we need to tell there container view controller to do so.
You can also check my previous answer here.
and Understanding UIViewController rotation when embed in Container View Controllers.
I'am currently working on an project where we have a tab bar controller with 4 tabs, and where each tab have an navigation controller. On each of these navigation controller there is multiple viewcontrollers pushed on it.
I read a lot of post here and other places, and we have currently done the following:
Subclassed UITabbarcontroller
- (BOOL)shouldAutorotate
{
return [[[self.viewControllers objectAtIndex:self.selectedIndex]topViewController] shouldAutorotate];
}
- (NSUInteger) supportedInterfaceOrientations
{
return [[[self.viewControllers objectAtIndex:self.selectedIndex]topViewController]supportedInterfaceOrientations];
}
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
return [[[self.viewControllers objectAtIndex:self.selectedIndex]topViewController] shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
}
This work fine, if we in each of our viewcontrollers specify the following:
- (NSUInteger) supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
return YES;
}
This will lock it to Portrait as expected.
But now the real problem occurs!
If we in our viewcontroller on one of the tabs specify that it should rotate to landscape it works fine, but when we then change tab it is still in landscape, which is not what we want!
So to sum up, have anyone got a solution to how you lock almost all views to a given orientation expect one, and can change tabs where they are in the orientation you specified (here portrait)?
I also read this post iOS 6 UITabBarController supported orientation with current UINavigation controller, but as one comment also mentioned "This is almost working for me. The problem is if I am already in landscape when I switch tabs to a portrait only view it is still in landscape. Rotating portrait fixes it and it won't rotate back to landscape, but I still need it be in portrait when it first loads" which almost is the same here..
I myself had this problem and i worked out a solution, its not pretty but it works.
In your TabbarController subclass implement this tabbarcontroller delegate function (remember to set delegate):
-(void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
int selected = self.selectedIndex;
UIViewController *con = [[UIViewController alloc] initWithNibName:#"XIBName" bundle:nil];
[[self.viewControllers objectAtIndex:selected] pushViewController:con animated:NO];
[[self.viewControllers objectAtIndex:selected]popViewControllerAnimated:NO];
[[self.viewControllers objectAtIndex:selected] setDelegate:nil];
}
The push and pop on the uinavigationcontroller in the tabs navigationcontroller will make the tabbarcontroller fire its Orientations functions again, and if you implemented the orientations code correctly it will change to your desired orientation.
i hope this helps, please fell free to comment if i need to explain anything in details.
Best Regards
Morten
What else should I do?
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation) toInterfaceOrientation
{
return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
-(BOOL)shouldAutoRotate
{
return NO;
}
My viewController still rotates.
It is embedded in a navigation stack.
If I subclass UINavigationController, and implement the same portrait-only templates there, and I embed my viewController in that tweaked navigationController, than it works, but I have no intention to rewrite my code everywhere a UINavigationController appears.
What is the best practice here?
ORIGINAL ANSWER: No need to subclass - just do a category like I described in my solution here:
Top-home button portrait orientation in iOS6 simulator not working
Basically, for iPhone the UINavigationController allows rotation for everything except "top home button portrait", for iPad it allows everything.
So either you do a category forwarding the decision to the currently active view controller or something static like
UINavigationController-Rotation.h:
#interface UINavigationController (Rotation)
#end
UINavigationController-Rotation.m:
#import "UINavigationController-Rotation.h"
#implementation UINavigationController (Rotation)
#pragma From UINavigationController
- (BOOL)shouldAutorotate {
return NO;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
#pragma -
#end
UPDATE: As Javier Soto pointed out, this might lead to undefined behavior if there is a second category doing the same. In that case, subclassing might be a better solution.
In a situation where you know there is no other category doing the same I still consider this a working, low effort, local and pragmatic solution. I am not religious about that. Decide yourself.
You should inherit from UINavigationController and use your custom one everywhere. It's not that much work (just search for occurrences of UINavigationController in your code). This will turn out to be much more flexible cause you'll be able to customize other things if necessary.
NEVER do it in a category that overrides methods in the main class like that other response suggests.