Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UINavigationController and autorotation

Tags:

I have a UIViewController that returns YES in shouldAutorotateToInterfaceOrientation: for UIDeviceOrientationPortrait and NO for everything else. With that view on the top of the stack, I use pushViewController:animated: to push a new UIViewController. The new controller returns YES for anything in shouldAutorotateToInterfaceOrientation:.

The first view refuses to rotate (as expected). Once the second view is pushed, the user can rotate the device and the UI will rotate (also as expected). If the second view is in landscape mode and the user presses the back button (which calls popViewControllerAnimated:), the first view will appear rotated (unexpected!).

If the user rotates the device back to portrait orientation, the view will rotate and then be stuck in portrait mode as before. This works but it's ugly for the user until they rotate back. So I'm in search of a way to make this view stay in portrait mode.

The only workaround I have found so far is to use -[UIDevice setOrientation:], which throws a warning (orientation is read-only) but works since it is actually defined. This is a huge hack and I'd like a real solution. In search of a real solution I attached GDB to the Photos application (MobileSlideshow.app) and discovered that it too uses -[UIDevice setOrientation:]. Being an internal application though I guess they have different rules.

Is there a correct way to achieve the expected autorotation behavior?

like image 262
Steven Canfield Avatar asked Jun 09 '09 14:06

Steven Canfield


2 Answers

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); } 
like image 191
Robin Avatar answered Sep 27 '22 20:09

Robin


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.

  1. An appViewController is added to the window as the host viewController, i.e. rooter than the RootViewController;
  2. A navigationController is added to the appViewController, with delegate set to appViewController;
  3. In the AppViewController


- (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];     } } 
like image 36
xingzhi.sg Avatar answered Sep 27 '22 19:09

xingzhi.sg