How to get different autorotation function in different UIViewController in iOS 6 [duplicate] - iphone

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!

Related

iOS7 - AutoRotate is not getting invoked

Team,
I am working on a support project and I have basic knowledge on iPhone. When I change the Orientation in my device, none of the below methods gets invoked at the time of Orientation change.
- (BOOL)shouldAutorotate {
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait) {
// your code for portrait mode
}
return YES;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskPortrait;
//return here which orientation you are going to support
}
I am pushing the screen through presentViewController and above methods gets invoked before pushing the screen, but nothing after the screen is pushed and when I change the orientation.
Also, I tried creating a Category as explained below, but I am getting viewController not found error, not sure what I am missing here...
In IOS7.If you use a UINavigationController, the rotate processing way is different!
See UINavigationController, can see it is a subclass of UIViewController, then that is in him there are listed in the above the rotation of the processing method; So wo need to use UINavigationController also do rotate processing,
My way is to add a Category to UINavigationController, do so.
In the Category. M writes
-(BOOL)shouldAutorotate {
return [[self.viewControllers lastObject] shouldAutorotate];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
any help is greatly appreciated!
For me the other two functions are also working but you can try to use the delegates below
From Apple Docs:
Sent to the view controller just before the user interface begins
rotating.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration
Sent to the view controller after the user interface rotates:
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation

Force landscape for one view controller ios

I've looked at several answers to questions similar but none of the answer worked. I have an app where I need everything portait except for one photo viewer I have. In the supported orientations section of the targets menu I only have portrait. How do I force my one view to be landscape. It is being pushed onto the stack from a nav controller but I'm using storyboards to control all that.
Since the answer seems to be hidden in the comments of the question and since ArunMak's answer is quite confusing, I'll just offer what I found out:
All I had to do was to add this function to my custom UIViewController subclass for the view:
- (NSUInteger)supportedInterfaceOrientations {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
// iPad: Allow all orientations
return UIInterfaceOrientationMaskAll;
} else {
// iPhone: Allow only landscape
return UIInterfaceOrientationMaskLandscape;
}
}
Note that the project needs to allow all orientations (that is: Portrait, Landscape Left, Landscape Right - but NEVER Upside Down on an iPhone!).
If you want to limit some or most views to Portrait, you need to implement the above method in every of those view controllers (or use a common super class for it and subclass all others from it) — if you limit the Device Orientation in the Info.plist to just Portrait, the app will never even think of going into landscape.
Yes this is possible, you can use this code:
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return UIInterfaceOrientationMaskLandscape;
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
return orientation==UIInterfaceOrientationMaskLandscape;
}
OR
Try this method in your app delegate
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
if (sglobalorientation isEqualToString:#"AllOrientation"]) {
return UIInterfaceOrientationMaskLandscape;
} else {
return UIInterfaceOrientationMaskAll;
}
}
you need to change the variable value sglobalorientation to that string value AllOrientation before you move to that Landscape view controller
and in your Landscape view controller, use this code in your view will appear
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft];
DigitalSignatureViewController *digisign = [[DigitalSignatureViewController alloc]init];
[self presentModalViewController:digisign animated:NO];
[self dismissModalViewControllerAnimated:NO];
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return NO;
}
and again when you move to next view controller change the sglobalorientation string value and follow the same step in your next view controller.
Lets try this code:
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeRight;
self.navigationController.view.center = CGPointMake(([UIScreen mainScreen].bounds.size.width/2), [UIScreen mainScreen].bounds.size.height/2);
CGFloat angle = 90 * M_PI / 180;
self.navigationController.view.transform = CGAffineTransformMakeRotation(angle);
self.navigationController.view.bounds = CGRectMake(0, 0,[UIScreen mainScreen].bounds.size.height , [UIScreen mainScreen].bounds.size.width);

I want to restrict some of the view controller to landscape in ios6

I'm trying to restrict one view controller which on top of UINavigationController. To do that i've created a UINavigationController subclass and implemented 2 methods
- (BOOL)shouldAutorotate {
return [[self.viewControllers lastObject] shouldAutorotate];}
- (NSUInteger)supportedInterfaceOrientations {
return [[self.viewControllers lastObject] supportedInterfaceOrientations];}
I want the first viewcontroller on top of UINavigationController(which is Root View Controller) should be in portrait mode and the next view controller which i'm pushing from the root view controller should be Landscape mode(ONLY).
So i'm overriding those two methods in both view controllers.
In the root view controller
- (BOOL)shouldAutorotate {
return NO;}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;}
In the next view controller
- (BOOL)shouldAutorotate {
return YES;}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;}
Its working fine but not completely. For the first time when I push the view controller its showing in portrait mode(Not restricting to landscape as I expected) and once I rotate the device/simulator and its working fine and restricting to landscape only.
Can anyone help in this?
Try this out.
Call this one in the viewWillAppear will explicitly tell the device to jump to the portrait orientation.
[[UIDevice currentDevice] performSelector:NSSelectorFromString(#"setOrientation:") withObject:(id)UIInterfaceOrientationPortrait];
I don't think this is the right solution. But if you got no other options, you can use this.
Happy Coding :)
U present new controller :
SecondViewController *objSecondViewController = [[SecondViewController alloc]initWithNibName:#"SecondViewController" bundle:nil];
[self.navigationController presentViewController:objSecondViewController animated:NO completion:^{}];
In new controller :
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscapeLeft;
}
This worked for me. Just try using this :
1) YourApp-Info.plist
Add one more array for Supported interface orientations
Add your required orientation to this dictionary
Refer below screenshot :
2) Project Target
Select the required orientation from Supported Interface Orientations
Refer below screenshot :
UIViewController have the following function. You can implement this in your view controller where you want to restict portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
if(interfaceOrientation == UIInterfaceOrientationPortrait)
return YES;
else
return NO;
}

Make a UiViewController stay in portrait mode iOS6 VS iOS5

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.

IOS6 rotation issue

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
}
}