views:

22106

answers:

14

I have an app where I would like to support device rotation in certain views but other don't particularly make sense in Landscape mode, so as I swapping the views out I would like to force the rotation to be set to portrait.

There is an undocumented property setter on UIDevice that does the trick but obviously generates a compiler warning and could disappear with a future revision of the SDK.

[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];

Are there any documented ways to force the orientation?

Update: I thought I would provide an example as I am not looking for shouldAutorotateToInterfaceOrientation as I have already implemented that.

I want my app to support landscape and portrait in View 1 but only portrait in View 2. I have already implemented shouldAutorotateToInterfaceOrientation for all views but if the user is in landscape mode in View 1 and then switches to View 2, I want to force the phone to rotate back to Portrait.

A: 

If you are using UIViewControllers, there is this method:

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation

Return NO for the view controllers containing the views you don't want to rotate.

More info here

Martin Gordon
That stops rotation, what I am looking for is to force a rotation back to portrait if the user is in landscape.I have added an example above to say what I mean better.
Dave Verwer
A: 

I don't think this is possible to do at run-time, though you could of course just apply a 90 degree transform to your UI.

Andrew Grant
It is possible with the code I originally posted, you just never know when that might stop working ;)
Dave Verwer
+3  A: 

I have this exact same problem. I think we should probably file a bug with Apple asking them to expose the API?

rustyshelf
+6  A: 

To rotate a view, you just need to set the status bar orientation and transform the view's coordinates appropriately. Here is some example code.

Setting the status bar is easy:

[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeRight;

Updating the view's transform is a little bit tricker, because you need to take the view's center into account and provide a matrix which rotates around that point.

benzado
+2  A: 

This is what I use. (You get some compile warnings but it works in both the Simulator and the iPhone)

[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight];
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeRight];
kdbdallas
A: 

[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeRight;

This doesn't work anymore as of 0ctober 21, 2008, iPhone OS 2.1. setOrientation is nolonger a UIApplication method.

Should be a comment, not an answer.
benzado
+5  A: 

From what I can tell, the setOrientation: method doesn't work (or perhaps works no longer). Here's what I'm doing to do this:

first, put this define at the top of your file, right under your #imports:

#define degreesToRadian(x) (M_PI * (x) / 180.0)

then, in the viewWillAppear: method

[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];  
if (self.interfaceOrientation == UIInterfaceOrientationPortrait) { 
 self.view.transform = CGAffineTransformIdentity;
 self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
 self.view.bounds = CGRectMake(0.0, 0.0, 480, 320);
}

if you want that to be animated, then you can wrap the whole thing in an animation block, like so:

[UIView beginAnimations:@"View Flip" context:nil];
[UIView setAnimationDuration:1.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

[[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];  
if (self.interfaceOrientation == UIInterfaceOrientationPortrait) { 
 self.view.transform = CGAffineTransformIdentity;
 self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
 self.view.bounds = CGRectMake(0.0, 0.0, 480, 320);
}
[UIView commitAnimations];

Then, in your portrait mode controller, you can do the reverse - check to see if its currently in landscape, and if so, rotate it back to Portrait.

Bdebeez
A: 

Hi,

I am faced with same problem and I tried you solution Bdebeez. But the results are wierd. Here is my code sample..

[UIView beginAnimations:@"View Flip" context:nil];
[UIView setAnimationDuration:1.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

if(self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft || self.interfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
  self.view.transform = CGAffineTransformIdentity;
  self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
  self.view.frame = CGRectMake(0.0, 0.0, 320,480 );
 mainSettingsView.frame = CGRectMake(0.0, 0.0, 320,480 );


 NSLog(NSStringFromCGRect(mainSettingsView.frame));
 NSLog(NSStringFromCGRect(self.view.frame));
}

[UIView commitAnimations];

And here is the console listing...

2009-02-27 13:52:34.038 NavControllerOrientationTest[610:20b] {{-2.09815e-05, -1.39876e-05}, {320, 480}}
2009-02-27 13:52:34.039 NavControllerOrientationTest[610:20b] {{-2.09815e-05, -1.39876e-05}, {320, 480}}

For me those two NSLog outputs are unexplained. Could anyone throw some light on why the x and y coordinates are haywire? As you may have guessed by now, the view is not visible.

Thanks and regards,

Sreejit

+5  A: 

If you want to force it to rotate from portrait to landscape here is the code. Just note that you need adjust the center of your view. I noticed that mine didn't place the view in the right place. Otherwise, it worked perfectly. Thanks for the tip.

if(UIInterfaceOrientationIsLandscape(self.interfaceOrientation)){

     [UIView beginAnimations:@"View Flip" context:nil];
     [UIView setAnimationDuration:0.5f];
     [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];

     self.view.transform = CGAffineTransformIdentity;
     self.view.transform = CGAffineTransformMakeRotation(degreesToRadian(90));
     self.view.bounds = CGRectMake(0.0f, 0.0f, 480.0f, 320.0f);
     self.view.center = CGPointMake(160.0f, 240.0f);

     [UIView commitAnimations];
}
Michael Gaylord
+3  A: 

This is no longer an issue on the later iPhone 3.1.2 SDK. It now appears to honor the requested orientation of the view being pushed back onto the stack. That likely means that you would need to detect older iPhone OS versions and only apply the setOrientation when it is prior to the latest release.

It is not clear if Apple's static analysis will understand that you are working around the older SDK limitations. I personally have been told by Apple to remove the method call on my next update so I am not yet sure if having a hack for older devices will get through the approval process.

It is not the case of iPhone Simulator 3.3.2. It is still not working.
Digital Robot
@user How do you request an orientation when you push a view on the stack?
progrmr
A: 

Take a look at my sample: link text

strange99
Your website is ugly.
Sneakyness
+1  A: 

I was having an issue where I had a UIViewController on the screen, in a UINavigationController, in landscape orientation. When the next view controller is pushed in the flow, however, I needed the device to return to portrait orientation.

What I noticed, was that the shouldAutorotateToInterfaceOrientation: method isn't called when a new view controller is pushed onto the stack, but it is called when a view controller is popped from the stack.

Taking advantage of this, I am using this snippet of code in one of my apps:

- (void)selectHostingAtIndex:(int)hostingIndex {

    self.transitioning = YES;

    UIViewController *garbageController = [[[UIViewController alloc] init] autorelease];
    [self.navigationController pushViewController:garbageController animated:NO];
    [self.navigationController popViewControllerAnimated:NO];

    BBHostingController *hostingController = [[BBHostingController alloc] init];
    hostingController.hosting = [self.hostings objectAtIndex:hostingIndex];
    [self.navigationController pushViewController:hostingController animated:YES];
    [hostingController release];

    self.transitioning = NO;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    if (self.transitioning)
        return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
    else
        return YES;
}

Basically, by creating an empty view controller, pushing it onto the stack, and immediately popping it off, it's possible to get the interface to revert to the portrait position. Once the controller has been popped, I just push on the controller that I intended to push in the first place. Visually, it looks great - the empty, arbitrary view controller is never seen by the user.

Andrew Vilcsak
Hi, I just implemented your method to force a VC in the correct orientation before pushing it, but I have found that it only works when forcing Portrait orientations (if the current view is Portrait and you wish to force the next into landscape, it won't call shouldAutorotateToInterfaceOrientation: when popping).Have you found a workaround to force a view to Landscape ?
nobre
A: 

I found a solution and wrote something in french (but code are in english). here

The way is to add the controller to the window view (the controller must possess a good implementation of the shouldRotate.... function).

Vinzius
A: 

I've been digging and digging looking for a good solution to this. Found this blog post that does the trick: remove your outermost view from the key UIWindow and add it again, the system will then re-query the shouldAutorotate methods from your viewcontrollers, enforcing the correct orientation to be applied. See it : http://goodliffe.blogspot.com/2009/12/iphone-forcing-uiview-to-reorientate.html

nobre