How to force view controller orientation in iOS 8?

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.

  • Images.xcassets bug upon Archive or Build for running
  • Pass a method from my custom cells class to the main class
  • What events fire with am iOS Low Battery warning? It's resetting my app
  • Setting default tab in UITabBar in swift
  • How can I render line faster than CGContextStrokePath?
  • Managing multiple UIViews from one UIViewController
  • 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!!

    22 Solutions Collect From Internet About “How to force view controller orientation in iOS 8?”

    For iOS 7 – 10:

    Objective-C:

    [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) forKey:@"orientation"];
    

    Swift 3:

    let value = UIInterfaceOrientation.landscapeLeft.rawValue
    UIDevice.current.setValue(value, forKey: "orientation")
    

    Just call it in - viewDidAppear: of the presented view controller.

    Orientation rotation is a little more complicated if you are inside a UINavigationController or UITabBarController. The problem is that if a view controller is embedded in one of these controllers the navigation or tab bar controller takes precedence and makes the decisions on autorotation and supported orientations.

    I use the following 2 extensions on UINavigationController and UITabBarController so that view controllers that are embedded in one of these controllers get to make the decisions.

    Give View Controllers the Power!

    Swift 2.3

    extension UINavigationController {
        public override func supportedInterfaceOrientations() -> Int {
            return visibleViewController.supportedInterfaceOrientations()
        }
        public override func shouldAutorotate() -> Bool {
            return visibleViewController.shouldAutorotate()
        }
    }
    
    extension UITabBarController {
        public override func supportedInterfaceOrientations() -> Int {
            if let selected = selectedViewController {
                return selected.supportedInterfaceOrientations()
            }
            return super.supportedInterfaceOrientations()
        }
        public override func shouldAutorotate() -> Bool {
            if let selected = selectedViewController {
                return selected.shouldAutorotate()
            }
            return super.shouldAutorotate()
        }
    }
    

    Swift 3

    extension UINavigationController {
        open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
            return visibleViewController?.supportedInterfaceOrientations ?? super.supportedInterfaceOrientations
        }
    
        open override var shouldAutorotate: Bool {
            return visibleViewController?.shouldAutorotate ?? super.shouldAutorotate
        }
    }
    
    extension UITabBarController {
        open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
            if let selected = selectedViewController {
                return selected.supportedInterfaceOrientations
            }
            return super.supportedInterfaceOrientations
        }
    
        open override var shouldAutorotate: Bool {
            if let selected = selectedViewController {
                return selected.shouldAutorotate
            }
            return super.shouldAutorotate
        }
    }
    

    Now you can override the supportedInterfaceOrientations method or you can override shouldAutoRotate in the view controller you want to lock down otherwise you can leave out the overrides in other view controllers that you want to inherit the default orientation behavior specified in your app’s plist

    Disable Rotation

    class ViewController: UIViewController {
        override func shouldAutorotate() -> Bool {
            return false
        }
    }
    

    Lock to Specific Orientation

    class ViewController: UIViewController {
        override func supportedInterfaceOrientations() -> Int {
            return Int(UIInterfaceOrientationMask.Landscape.rawValue)
        }
    }
    

    In theory this should work for all complex view controller hierarchies, but I have noticed an issue with UITabBarController. For some reason it wants to use a default orientation value. See the following blog post if you are interested in learning about how to work around some of the issues:

    Lock Screen Rotation

    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
    }
    

    This is what worked for me:

    https://developer.apple.com/library//ios/documentation/UIKit/Reference/UIViewController_Class/index.html#//apple_ref/occ/clm/UIViewController/attemptRotationToDeviceOrientation

    Call it in your viewDidAppear: method.

    - (void) viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
    
        [UIViewController attemptRotationToDeviceOrientation];
    }
    

    This way work for me in Swift 2 iOS 8.x:

    PS (this method dont require to override orientation functions like shouldautorotate on every viewController, just one method on AppDelegate)

    Check the “requires full screen” in you project general info.
    enter image description here

    So, on AppDelegate.swift make a variable:

    var enableAllOrientation = false
    

    So, put also this func:

    func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
            if (enableAllOrientation == true){
                return UIInterfaceOrientationMask.All
            }
            return UIInterfaceOrientationMask.Portrait
    }
    

    So, in every class in your project you can set this var in viewWillAppear:

    override func viewWillAppear(animated: Bool)
    {
            super.viewWillAppear(animated)
            let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
            appDelegate.enableAllOrientation = true
    }
    

    If you need to make a choices based on the device type you can do this:

    override func viewWillAppear(animated: Bool)
        {
            super.viewWillAppear(animated)
            let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
            switch UIDevice.currentDevice().userInterfaceIdiom {
            case .Phone:
            // It's an iPhone
               print(" - Only portrait mode to iPhone")
               appDelegate.enableAllOrientation = false
            case .Pad:
            // It's an iPad
               print(" - All orientation mode enabled on iPad")
               appDelegate.enableAllOrientation = true
            case .Unspecified:
            // Uh, oh! What could it be?
               appDelegate.enableAllOrientation = false
            }
        }
    

    This should work from iOS 6 on upwards, but I’ve only tested it on iOS 8. Subclass UINavigationController and override the following methods:

    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
        return UIInterfaceOrientationLandscapeRight;
    }
    
    - (BOOL)shouldAutorotate {
        return NO;
    }
    

    Or ask the visible view controller

    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
        return self.visibleViewController.preferredInterfaceOrientationForPresentation;
    }
    
    - (BOOL)shouldAutorotate {
        return self.visibleViewController.shouldAutorotate;
    }
    

    and implement the methods there.

    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];
    }
    

    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
    }
    

    If you are using navigationViewController you should create your own superclass for this and override:

    - (BOOL)shouldAutorotate {
      id currentViewController = self.topViewController;
    
      if ([currentViewController isKindOfClass:[SecondViewController class]])
        return NO;
    
      return YES;
    }
    

    this will disable rotation in SecondViewController but if you push your SecondViewController when your device is on portrait orientation then your SecondViewController will appear in portrait mode.

    Assume that you are using storyboard. You have to create manual segue (How to) and in your “onClick” method:

    - (IBAction)onPlayButtonClicked:(UIBarButtonItem *)sender {
      NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
      [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
      [self performSegueWithIdentifier:@"PushPlayerViewController" sender:self];
    }
    

    This will force landscape orientation before your superclass disable autorotate feature.

    The combination of Sids and Koreys answers worked for me.

    Extending the Navigation Controller:

    extension UINavigationController {
        public override func shouldAutorotate() -> Bool {
            return visibleViewController.shouldAutorotate()
        }
    }
    

    Then disabling rotation on the single View

    class ViewController: UIViewController {
        override func shouldAutorotate() -> Bool {
            return false
        }
    }
    

    And rotating to the appropriate orientation before the segue

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if (segue.identifier == "SomeSegue")
        {
            let value = UIInterfaceOrientation.Portrait.rawValue;
            UIDevice.currentDevice().setValue(value, forKey: "orientation")
        }
    }
    

    The top solution above:

    let value = UIInterfaceOrientation.LandscapeLeft.rawValue
    UIDevice.currentDevice().setValue(value, forKey: "orientation")
    

    didnt’work for me when I called it in viewDidAppear of the presented view controller. However it did work when I called it in preparForSegue in the presenting view controller.

    (Sorry, not enough reputation points to comment on that solution, so I had to add it like this)

    My requirements are

    1. lock all views in portrait mode
    2. use AVPlayerViewController to play video

    When video is playing, if it’s a landscape then allow the screen to rotate landscape right and landscape left. If it’s a portrait then lock the view in portrait mode only.

    First, define supportedInterfaceOrientationsForWindow in AppDelegate.swift

    var portrait = true
    func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
        if portrait {
            return .Portrait
        } else {
            return .Landscape
        }
    }
    

    Second, in your main view controller, define following functions

    override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        print("\(#function)")
        return .Portrait
    }
    
    override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
        return .Portrait
    }
    
    override func shouldAutorotate() -> Bool {
        return false
    }
    

    Then, you need to subclass AVPlayerViewController

    class MyPlayerViewController: AVPlayerViewController {
    
        var size: CGSize?
    
        var supportedOrientationMask: UIInterfaceOrientationMask?
        var preferredOrientation: UIInterfaceOrientation?
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            if let size = size {
                if size.width > size.height {
                    self.supportedOrientationMask =[.LandscapeLeft,.LandscapeRight]
                    self.preferredOrientation =.LandscapeRight
                } else {
                    self.supportedOrientationMask =.Portrait
                    self.preferredOrientation =.Portrait
                }
            }
        }
    

    Override these three functions in MyPlayerViewController.swift

    override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return self.supportedOrientationMask!
    }
    
    override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
        return self.preferredOrientation!
    }
    

    Because user might rotate device landscape left or landscape right, we need to set auto rotate to be true

    override func shouldAutorotate() -> Bool {
        return true
    }
    

    Finally, create MyPlayerViewController instance in your view controller and set the property size value.

    let playerViewController = MyPlayerViewController()
    
    // Get the thumbnail  
    let thumbnail = MyAlbumFileManager.sharedManager().getThumbnailFromMyVideoMedia(......)
    
    let size = thumbnail?.size
    playerViewController.size = size
    

    Initiate your player with proper videoUrl, then assign your player to playerViewController. Happy coding!

    I have tried many solutions, but the one that worked for is the following:

    There is no need to edit the info.plist in ios 8 and 9.

    - (BOOL) shouldAutorotate {
        return NO;
    }   
    
    - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
        return (UIInterfaceOrientationPortrait | UIInterfaceOrientationPortraitUpsideDown);
    }
    

    Possible orientations from the Apple Documentation:

    UIInterfaceOrientationUnknown

    The orientation of the device cannot be determined.

    UIInterfaceOrientationPortrait

    The device is in portrait mode, with the device held upright and the
    home button on the bottom.

    UIInterfaceOrientationPortraitUpsideDown

    The device is in portrait mode but upside down, with the device held
    upright and the home button at the top.

    UIInterfaceOrientationLandscapeLeft

    The device is in landscape mode, with the device held upright and the
    home button on the left side.

    UIInterfaceOrientationLandscapeRight

    The device is in landscape mode, with the device held upright and the
    home button on the right side.

    According to Korey Hinton’s answer

    Swift 2.2:

    extension UINavigationController {
        public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
            return visibleViewController!.supportedInterfaceOrientations()
        }
        public override func shouldAutorotate() -> Bool {
            return visibleViewController!.shouldAutorotate()
        }
    }
    
    
    extension UITabBarController {
        public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
            if let selected = selectedViewController {
                return selected.supportedInterfaceOrientations()
            }
            return super.supportedInterfaceOrientations()
        }
        public override func shouldAutorotate() -> Bool {
            if let selected = selectedViewController {
                return selected.shouldAutorotate()
            }
            return super.shouldAutorotate()
        }
    }
    

    Disable Rotation

    override func shouldAutorotate() -> Bool {
        return false
    }
    

    Lock to Specific Orientation

    override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.Portrait
    }
    

    I tried a few solutions in here and the important thing to understand is that it’s the root view controller that will determine if it will rotate or not.

    I created the following objective-c project github.com/GabLeRoux/RotationLockInTabbedViewChild with a working example of a TabbedViewController where one child view is allowed rotating and the other child view is locked in portrait.

    It’s not perfect but it works and the same idea should work for other kind of root views such as NavigationViewController. 🙂

    Child view locks parent orientation

    According to solution showed by @sid-sha you have to put everything in the viewDidAppear: method, otherwise you will not get the didRotateFromInterfaceOrientation: fired, so something like:

    - (void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
        if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
            interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
            NSNumber *value = [NSNumber numberWithInt:interfaceOrientation];
            [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
        }
    }
    

    My solution

    In AppDelegate:

    func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
        if let topController = UIViewController.topMostViewController() {
            if topController is XXViewController {
                return [.Portrait, .LandscapeLeft]
            }
        }
        return [.Portrait]
    }
    

    XXViewController is the ViewController you want to support Landscape mode.

    Then Sunny Shah’s solution would work in your XXViewController on any iOS version:

    let value = UIInterfaceOrientation.LandscapeLeft.rawValue
    UIDevice.currentDevice().setValue(value, forKey: "orientation")
    

    This is the utility function to find the top most ViewController.

    extension UIViewController {
    
        /// Returns the current application's top most view controller.
        public class func topMostViewController() -> UIViewController? {
            let rootViewController = UIApplication.sharedApplication().windows.first?.rootViewController
            return self.topMostViewControllerOfViewController(rootViewController)
        }
    
    
    
        /// Returns the top most view controller from given view controller's stack.
        class func topMostViewControllerOfViewController(viewController: UIViewController?) -> UIViewController? {
            // UITabBarController
            if let tabBarController = viewController as? UITabBarController,
               let selectedViewController = tabBarController.selectedViewController {
                return self.topMostViewControllerOfViewController(selectedViewController)
            }
    
            // UINavigationController
            if let navigationController = viewController as? UINavigationController,
               let visibleViewController = navigationController.visibleViewController {
                return self.topMostViewControllerOfViewController(visibleViewController)
            }
    
            // presented view controller
            if let presentedViewController = viewController?.presentedViewController {
                return self.topMostViewControllerOfViewController(presentedViewController)
            }
    
            // child view controller
            for subview in viewController?.view?.subviews ?? [] {
                if let childViewController = subview.nextResponder() as? UIViewController {
                    return self.topMostViewControllerOfViewController(childViewController)
                }
            }
    
            return viewController
        }
    
    }
    

    Use this to lock view controller orientation, tested on IOS 9:

    // Lock orientation to landscape right

    -(UIInterfaceOrientationMask)supportedInterfaceOrientations {
        return UIInterfaceOrientationMaskLandscapeRight;
    }
    
    -(NSUInteger)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController {
        return UIInterfaceOrientationMaskLandscapeRight;
    }
    

    There still seems to be some debate about how best to accomplish this task, so I thought I’d share my (working) approach. Add the following code in your UIViewController implementation:

    - (void) viewWillAppear:(BOOL)animated
    {
        [UIViewController attemptRotationToDeviceOrientation];
    }
    
    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
    {
        return (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft);
    }
    
    -(BOOL)shouldAutorotate
    {
        return NO;
    }
    
    - (UIInterfaceOrientationMask)supportedInterfaceOrientations
    {
        return UIInterfaceOrientationMaskLandscapeLeft;
    }
    

    For this example, you will also need to set your allowed device orientations to ‘Landscape Left’ in your project settings (or directly in info.plist). Just change the specific orientation you want to force if you want something other than LandscapeLeft.

    The key for me was the attemptRotationToDeviceOrientation call in viewWillAppear – without that the view would not properly rotate without physically rotating the device.

    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.

    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)
    

    Thant’s all

    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
        [UIViewController attemptRotationToDeviceOrientation];
    }