Disable Autorotation on iOS 7 for a single presented ViewController - ios

I have a UIViewController, which is presented modally (full screen) and I would like to disable autorotation in that view. I do not want to restrict it to landscape or portrait, just would like it to stay in whatever orientation it was originally presented.
On iOS 6 it was sufficient to just override the method:
- (BOOL)shouldAutorotate {
return NO;
}
And it did exactly what I wanted. On iOS 7 however, this seems to have no effect. The method does get called, but the return value seems to be ignored by the OS - it auto rotates no matter what.
The documentation does not mention any changes to this method. How can I achieve the desired effect on iOS 7?
Edit: the view controller is being presented (not pushed!) by a UINavigationViewController:
[self.navigationController presentViewController:vc animated:YES completion:nil];
Solution:
As odd as it may seem, but this solution was not published in the numerous existing questions on this topic. On iOS 7 it seems the answer the UINavigationController gives to
shouldAutorotate is what the OS acts on. We need to subclass UINavigationController to modify its behaviour.
When dealing with a regular navigation stack it is indeed sufficient to just use [self.topViewController shouldAutorotate], but when there is modal view, it resides in self.presentedViewController, not self.topViewController. Thus the full solution looks like:
- (BOOL)shouldAutorotate {
UIViewController *vc;
if (self.presentedViewController) vc = self.presentedViewController;
else vc = [self topViewController];
return [vc shouldAutorotate];
}

So I just tried you code and it worked which leads me to believe that you are presenting your UIViewController in a UINavigationController. For whatever reason, iOS 7 changed how UINavigationController handle rotations.
The easiest solution is to create a subclass of UINavigationController that overrides the shouldAutorotate method and returns the value from the topViewController.
#interface CustomNavigationController : UINavigationController
#end
#implementation CustomNavigationController
- (BOOL)shouldAutorotate
{
return [[self topViewController] shouldAutorotate];
}
#end
So instead of doing this, where viewController is your object that return NO for shouldAutorotate.
UINavigaitonController *navController = [UINavigationController alloc] initWithRootViewController:viewController];
[self presentViewController:navController animated:YES completion:nil];
You would use the CustomNavigationController instead
CustomNavigationController *customNavController = [CustomNavigationController alloc] initWithRootViewController:viewController];
[self presentViewController:customNavController animated:YES completion:nil];

#import <objc/message.h>
-(void)viewDidAppear:(BOOL)animated{
objc_msgSend([UIDevice currentDevice], #selector(setOrientation:), UIInterfaceOrientationPortrait );
}
see How do I programmatically set device orientation in iOS7?
But take the case you are using this method because it is a private API, and your app may be rejected by Apple. So, maybe is better to set orientation from Project details -> General -> Deployment Info tab, choose Landscape Left and Landscape Right choice only. This can be a better approach, if all your views need only one kind of orientation.

Related

Disable rotation of a UINavigationController in iOS7

I have a UINavigationController which creates an instance of UIViewController and sets another UIViewController as it's root. I then present the navigation controller and everything works.
The problem comes when the user rotates the device. All my controllers (including root and excluding UINavigationController) implement the shouldAutorotate and return FALSE.
Somehow, my views still rotate. We come to the centre of my problem. I create and present the navigation controller like so:
AwesomeViewController *controller = [[AwesomeViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller];
[self presentViewController:navController
animated:YES
completion:NULL];
It is pretty straight forward as you can see.
The navController is missing its shouldAutorotate set to FALSE and that's why it's rotating, am I correct?
How to lock it in portrait orientation?
Can it be done without making a ridiculous subclass like this:
#interface LockedRotationNavigationController : UINavigationController
#end
#implementation LockedRotationNavigationController
- (BOOL)shouldAutorotate { return NO; }
#end
What I'm actually looking for is how to disable rotation of the UINavigationController without subclassing it?
The property is readonly so I'm out of ideas on how to do it.
To disable rotations globally in your app in Info.plist expand Supported interface orientations and remove Landscape items to make your application only run in portrait mode.
Or you can just select supported orientations in Xcode like this:
You can also lock the rotation programmatically by implementing shouldAutorotateToInterfaceOrientation method in your view controller.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}

After forcing an orientation of an IOS 7 app to Portrait, nothing on the screen becomes tappable

I am seeing an issue that only happens on IOS 7 and not IOS 6, i.e. the same code works on 6 but not 7.
Basically, there is this view that I need to transition to that must be in portrait mode.
So, like many of the other examples I found on StackOverflow, the way to force an orientation would be to call this on the navigation controller :
UIViewController *mvc = [[UIViewController alloc] init];
[self presentViewController:mvc animated:NO completion:^{
[self dismissViewControllerAnimated:YES completion:^{
NSLog(#"Cleared");
[mvc.view removeFromSuperview];
[mvc removeFromParentViewController];
}];
}];
And in the target controller, I have:
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotate
{
return NO;
}
However, once I transition to this target UIViewController, nothing on that UIViewController becomes tappable at all. I am very sure that the UIButtons are linked properly because if I just load up this controller as the first controller when the app starts, the buttons all worked fine, but if I have to force an orientation on it before showing it, then the buttons don't work
I was wondering if anyone else has encountered such an issue in IOS 7 as well? This works perfectly in IOS 6
Thanks
IS

Trying to subclass navigation controller in order to load external view Controller with separate .xib file in landscape mode in iOS

I have an iPad application which I have created using storyboards. I have created another single viewController which I have created using a separate .xib file. This viewController I need to call from the main application, and then later dismiss to return back to the main application. I am able to do this so far. My problem is that because I am using a Navigation Controller to call this secondary view controller, I am unable to load this view controller in landscape mode. I am only able to load it in portrait mode. Based on going through this forum, and from whatever research that I have done, I have learned that I need to subclass the navigation controller, and then that is how I will be able to load this secondary view controller in landscape mode.
I have included the following methods in my secondary view controller (NextViewController), but it has no effect:
-(BOOL)shouldAutorotate
{
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
Here is the code in the calling viewController (MainViewController), which calls NextViewController, which in turn is appearing in portrait mode, instead of the desired landscape mode:
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
_nextView = [[NextLandscapeViewController alloc] initWithNibName:#"NextLandscapeViewController" bundle:nil];
[_nextView setDelegate:(id)self];
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:_nextView];
[self presentViewController:navigationController animated:YES completion:nil];
}
As I pointed out, the solution that I need is to subclass the Navigation Controller, but I honestly have never done this before, and nor do I know how to do it. Can someone show me how to do it so that I can call NextViewController, and have it displayed in landscape mode?
Thanks in advance to all who reply.
For subclass from Navigation Controller for orientation, you can try this code (as example):
// .h - file
#interface MyNavigationController : UINavigationController
#end
// .m - file
#import "MyNavigationController.h"
#implementation MyNavigationController
-(BOOL)shouldAutorotate
{
return [self.topViewController shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations
{
return [self.topViewController supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [self.topViewController preferredInterfaceOrientationForPresentation];
}
#end
upd: (This code work on ios6)

Tabbar controller with navigationcontrollers orientation ios 6

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

iOS 6 orientation issues

I have an application which normally is a portrait app and only show landscape view for one UIViewController. It works fine until the new iOS 6 is released.
I really don't understand how orientation works in iOS 6. So I wrote a testing app. Here is what I did:
Set the orientation of the application to support all orientations.
I'm using story board. The rootViewController is embedded in UINavigationController which is in portrait.
The code in rootViewController:
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
}
When I clicked the Open bar button, I'll push another (SecondViewController) view controller which supposed to be in landscape mode:
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
}
Although this method is called correctly, the second view controller is always also in portrait mode.
Can anybody give me some suggestions? Thanks
Here is my solution:
In second view controller's viewDidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
UIViewController *viewController = [[UIViewController alloc] init];
[self presentViewController:viewController animated:NO completion:^{
[viewController dismissViewControllerAnimated:NO completion:nil];
}];
}
This will force the second view to rotate to landscape orientation which solved my problem. And it works for iOS 5 and 6.
For iOS-6, I have done this. It is running fine
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
return UIInterfaceOrientationLandscapeLeft;}
In second View
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
return YES;}
I think that best solution is to stick to official apple documentation. So according to that I use following methods and everything is working very well on iOS 5 and 6.
In all of your ViewControllers override following methods.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
Methods for iOS 6, first method returns supported orientation mask (as their name indicate), you can change it into Landscape or what suites you best.
-(NSInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait; //UIInterfaceOrientationMaskPortrait or LandscapeLeft ...
}
second one thats tells your VC which is preferred interface orientation when VC is going to be displayed.
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait; //tells your VC in which orientation it should be presented, if you set Porttrait it would be in Portrait or otherwise ...
}
This solution is working smooth, I dont like the idea of creating macros and other stuffs, that goes around this simple solution.
Hope this help...

Resources