Question

I have an UINavigationController subclass as the root viewController in my app. I push various other view controllers onto the nav controller's viewControllers stack.

Some of these are allowed to be displayed in any orientation, some are landscape only.

I have used the information at this link: http://grokin.gs/blog/2012/11/16/orientationrespectfulnavigationcontroller/ so that when a rotation of the device happens, the navigation controller correctly reports the top view controller's allowed orientations. However, I can't seem to work out how to trigger autorotation when a new view controller is pushed on to the stack.

e.g. The root vc of the app is my naviationViewController. It's initial top vc can be displayed either in landscape or portrait. Say the device is in portrait, now I push another vc onto the navigation controller. This new vc can only be displayed in landscape. If the device is rotated though 360 degrees, then the correct things happen, i.e. the navController limits rotation to landscape. However if the device is never moved, then supportedInterfaceOrientations: is not called on the navController, so the device doesn't know that the UI should be rotated to landscape automatically.

So - can I trigger the UIApplication to do whatever it does when rotation happens? Or am I missing something?

Thanks.

Was it helpful?

Solution

So, it appears that you can trigger the system's re-evaluation of which device orientations are allowed. This will happen whenever a viewController is presented full-screen modally.

Therefore, (it's a nasty hack but it appears to work) you can ask the root VC to present a VC which only allows landscape orientations, and then as soon as it has been presented, dismiss it again.

e.g. see code below:

- (void)viewDidAppear:(BOOL)animated
{
    //  Force an orientation update if we are not in landscape...

    BOOL force = YES;
    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
    if (orientation == UIDeviceOrientationLandscapeRight || orientation == UIDeviceOrientationLandscapeLeft)
    {
        force = NO;
    }

    if (force)
    {
        PortraitOnlyViewController *newVC = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"forceLandscape"];

        [self.navigationController presentViewController:newVC animated:NO completion:^(void){
            NSLog(@"forcing VC has been presented.");
            [self.navigationController dismissViewControllerAnimated:NO completion:^(void){
                NSLog(@"forcing VC has been dismissed");
            }];
        }];

    }

}

OTHER TIPS

I had to modify Diggory's answer because I am not using a UINavigationController, and also using Swift. I also got the following error on iOS 8.3:

Trying to dismiss the presentation controller while transitioning already. (<_UIFullscreenPresentationController

Which was resolved by adding a call to dispatch_get_main_queue.

class ViewController: UIViewController {
    var allowTurning = false

    override func supportedInterfaceOrientations() -> Int {
        if allowTurning {
            return Int(UIInterfaceOrientationMask.AllButUpsideDown.rawValue)
        } else  {
            return Int(UIInterfaceOrientationMask.Portrait.rawValue)
        }
    }

    @IBAction func doTurn(sender: UIButton) {
        allowTurning = true
        let fakeViewController = UIViewController()
        presentViewController(fakeViewController, animated: false) {
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                self.dismissViewControllerAnimated(false, completion: {
                    NSLog("Done dismissing!")
                })
            })
        }
    }

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top