I have a UIViewController that returns YES
in shouldAutorotateToInterfaceOrientation:
for UIDeviceOrientationPortrait
and NO
A legal alternative to UIDevice setOrientation is as follows:
UIWindow* window = UIApplication.sharedApplication.keyWindow;
UIView* view = [window.subviews objectAtIndex:0];
[view removeFromSuperview];
[window addSubview:view];
This forces the current view controller to evaluate its orientation, calling shouldAutorotateToInterfaceOrientation and switching away from any prohibited orientations.
E.g. the following code would be used in a view controller that supports all orientations but whose parent only supports landscape:
- (void)popBack
{
[self.navigationController popToRootViewControllerAnimated:YES];
}
- (IBAction)backPressed:(id)sender
{
portraitOrientationPermitted = NO;
// Force the framework to re-evaluate the interface orientation.
UIWindow* window = UIApplication.sharedApplication.keyWindow;
UIView* view = [window.subviews objectAtIndex:0];
[view removeFromSuperview];
[window addSubview:view];
[self performSelector:@selector(popBack) withObject:nil afterDelay:0.8];
portraitOrientationPermitted = YES;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return portraitOrientationPermitted ||
UIInterfaceOrientationIsLandscape(interfaceOrientation);
}
I was about to tell you that there was probably no way, but then I had a thought. It would be difficult to get right, but you might be able to make it work if you used two separate UINavigationController
s: one that controls the root view and prohibits rotation, and one for the child views that allows it. You would manually handle the transition to and from the root controller and the child controller.
You'd have to patch up the child navigation controller to have the correct back button. And, of course, you'd have to handle the back button press yourself. You would probably have to use a dummy UINavigationBar
to do the animation from one navigation controller to the next so that the transition will look right. You would also have to animate the "push" transition between the navigation controllers, as well, which might take a bit of tweaking to get it to look right. You would have to:
UINavigationItem
and push it on)UINavigationItem
for the incoming view controller and push it on the dummy navigation barUINavigationBar
from the view, and also the outgoing navigation controller.All of this is a lot of work, but if you're very clever (and very tenacious), you might be able to get it to work. I'd love to see the result!
That said, you might be better off just using setOrientation:
and taking your chances with the App Store approval process ;-)
iOS 5 adds +[UIViewController attemptRotationToDeviceOrientation], which solves the problem for me.
It has been a old post but since it hasn't been solved. I would like to share my solution, for any others who might be in a headache.
Objective: A UINavigationController and most of viewcontrollers in its stack fixed at portrait, except for one viewcontroller in the stack being allowed to rotate to both portrait and landscape.
Problem: intuitively I set an selective shouldAutorotateToInterfaceOrientation by checking if the topViewController is the rotableViewController. However after poping back from the rotableViewController at the landscape mode, the navigationcontroller is now shown in the landscape mode although it is not allowed.
Solution:The killer is to disallow the rotation at viewWillAppear
and present & dismiss a modalViewController without animation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (interfaceOrientation == UIInterfaceOrientationPortrait) return YES;
return canRotate;
}
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[viewController viewDidAppear:animated];
canRotate = ([navigationController.topViewController isKindOfClass:[MyRotatable class]]);
}
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[viewController viewWillAppear:animated];
if (![navigationController.topViewController isKindOfClass:[MyRotatable class]]) {
canRotate = NO;
UIViewController * blanck = [[UIViewController alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:blanck animated:NO];
[self dismissModalViewControllerAnimated:NO];
[blanck release];
}
}
I found a nice workaround for this problem. The clue is to support all orientations for all views in UINavigationController
.
I've got 2 views in controller. Root view is to support only LandscapeRight
, and second supports both LandscapeRight
and Portrait
.
Second view shouldAutorotateToInterfaceOrientation
method looks as usual:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight) ||
(interfaceOrientation == UIInterfaceOrientationPortrait);
}
The workaround itself is contained in Root view source Now the root view rotates in terms of code, but the user cant see it.
//auxiliary function
-(void) fixOrientation:(UIInterfaceOrientation)orientation
{
if (orientation == UIInterfaceOrientationPortrait)
self.view.transform = CGAffineTransformMakeRotation(M_PI_2);
else if (orientation == UIInterfaceOrientationLandscapeRight)
self.view.transform = CGAffineTransformMakeRotation(0);
}
-(void) viewWillAppear:(BOOL)animated
{
[self fixOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
[self fixOrientation:interfaceOrientation];
//notice, that both orientations are accepted
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight) ||
(interfaceOrientation == UIInterfaceOrientationPortrait);
}
//these two functions helps to avoid blinking
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[UIView setAnimationsEnabled:NO]; // disable animations temporarily
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[UIView setAnimationsEnabled:YES]; // rotation finished, re-enable them
}
So this annoying bug comes a really long way from iOS 1 to iOS 4.
I believe the best solution we have it duplicate this bug and let Apple know we really want it fixed. I've just reported it again under the bug ID 8478525.