How to force view controller orientation in iOS 8?

2018-12-31 15:39发布

Before iOS 8, we used below code in conjunction with supportedInterfaceOrientations and shouldAutoRotate delegate methods to force app orientation to any particular orientation. I used below code snippet to programmatically rotate the app to desired orientation. Firstly, I am changing the status bar orientation. And then just presenting and immediately dismissing a modal view rotates the view to desired orientation.

[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:YES];
UIViewController *c = [[UIViewController alloc]init];
[self presentViewController:vc animated:NO completion:nil];
[self dismissViewControllerAnimated:NO completion:nil];

But this is failing in iOS 8. Also, I have seen some answers in stack overflow where people suggested that we should always avoid this approach from iOS 8 onwards.

To be more specific, my application is a universal type of application. There are three controllers in total.

  1. First View controller- It should support all orientations in iPad and only portrait (home button down) in iPhone.

  2. Second View controller- It should support only landscape right in all conditions

  3. Third View controller- It should support only landscape right in all conditions

We are using navigation controller for page navigation. From the first view controller, on a button click action, we are pushing the second one on stack. So, when the second view controller arrives, irrespective of device orientation, the app should lock in landscape right only.

Below is my shouldAutorotate and supportedInterfaceOrientations methods in second and third view controller.

-(NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscapeRight;
}

-(BOOL)shouldAutorotate {
    return NO;
}

Is there any solution for this or any better way of locking a view controller in particular orientation for iOS 8. Please help!!

24条回答
像晚风撩人
2楼-- · 2018-12-31 15:51

I found that if it's a presented view controller, you can override preferredInterfaceOrientationForPresentation

Swift:

override func supportedInterfaceOrientations() -> Int {
  return Int(UIInterfaceOrientationMask.Landscape.rawValue)
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
  return UIInterfaceOrientation.LandscapeLeft
}

override func shouldAutorotate() -> Bool {
  return false
}
查看更多
旧时光的记忆
3楼-- · 2018-12-31 15:51

This is a feedback to comments in Sid Shah's answer, regarding how to disable animations using:

[UIView setAnimationsEnabled:enabled];

Code:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:NO];
    [UIView setAnimationsEnabled:NO];

    // Stackoverflow #26357162 to force orientation
    NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight];
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:NO];
    [UIView setAnimationsEnabled:YES];
}
查看更多
美炸的是我
4楼-- · 2018-12-31 15:54

[iOS9+] If anyone dragged all the way down here as none of above solutions worked, and if you present the view you want to change orientation by segue, you might wanna check this.

Check your segue's presentation type. Mine was 'over current context'. When I changed it to Fullscreen, it worked.

Thanks to @GabLeRoux, I found this solution.

  • This changes only works when combined with solutions above.
查看更多
临风纵饮
5楼-- · 2018-12-31 15:54

I have the same problem and waste so many time for it. So now I have my solution. My app setting is just support portrait only.However, some screens into my app need have landscape only.I fix it by have a variable isShouldRotate at AppDelegate. And the function at AppDelegate:

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {
    if isShouldRotate == true {
        return Int(UIInterfaceOrientationMask.Landscape.rawValue)
    }
    return Int(UIInterfaceOrientationMask.Portrait.rawValue)
}

And finally when a ViewControllerA need landscape state. Just do that: before push/present to ViewControllerA assign isShouldRotate to true. Don't forget when pop/dismiss that controller assign isShouldRotate to false at viewWillDisappear.

查看更多
冷夜・残月
6楼-- · 2018-12-31 15:55

First of all - this is a bad idea, in general, something wrong going with your app architecture, but, sh..t happens, if so, you can try to make something like below:

final class OrientationController {

    static private (set) var allowedOrientation:UIInterfaceOrientationMask = [.all]

    // MARK: - Public

    class func lockOrientation(_ orientationIdiom: UIInterfaceOrientationMask) {
        OrientationController.allowedOrientation = [orientationIdiom]
    }

    class func forceLockOrientation(_ orientation: UIInterfaceOrientation) {
        var mask:UIInterfaceOrientationMask = []
        switch orientation {
            case .unknown:
                mask = [.all]
            case .portrait:
                mask = [.portrait]
            case .portraitUpsideDown:
                mask = [.portraitUpsideDown]
            case .landscapeLeft:
                mask = [.landscapeLeft]
            case .landscapeRight:
                mask = [.landscapeRight]
        }

        OrientationController.lockOrientation(mask)

        UIDevice.current.setValue(orientation.rawValue, forKey: "orientation")
    }
}

Than, in AppDelegate

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // do stuff
    OrientationController.lockOrientation(.portrait)
    return true
}

// MARK: - Orientation

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    return OrientationController.allowedOrientation
}

And whenever you want to change orientation do as:

OrientationController.forceLockOrientation(.landscapeRight)

Note: Sometimes, device may not update from such call, so you may need to do as follow

OrientationController.forceLockOrientation(.portrait)
OrientationController.forceLockOrientation(.landscapeRight)

That's all

查看更多
永恒的永恒
7楼-- · 2018-12-31 15:55

On Xcode 8 the methods are converted to properties, so the following works with Swift:

override public var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.portrait
}

override public var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
    return UIInterfaceOrientation.portrait
}

override public var shouldAutorotate: Bool {
    return true
}
查看更多
登录 后发表回答