1

I have an app where I set the orientation to be Portrait only in it's target settings:

enter image description here

In one particular view controller I'd like to override this setting so auto layout will update views when a device is rotated. I've tried these methods with no success:

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

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.AllButUpsideDown
}
Fred Faust
  • 6,696
  • 4
  • 32
  • 55
  • 1
    I'm afraid there is no way other than check all orientations in project settings and then restrict them in all view controllers except the required one – heximal Apr 01 '16 at 13:57
  • @heximal please see the accepted answer, it's quite nice and doesn't require updating all my VCs. – Fred Faust Apr 01 '16 at 14:13

2 Answers2

5

If I have read the accepted answer, I wouldn't have to write my own. My version is longer but only require app delegate's application(_:supportedInterfaceOrientationsFor:) method. It maybe suitable in situation where you can't change or prefer not to change the target view controllers. For example, a third party view controller.

I was inspired from Apple's official doc: supportedInterfaceOrientations

My app runs as Portrait on iPhone and all orientations on iPad. I only want one view controller (a JTSImageViewController presents an image for larger view) to be able to rotate.

Info.plist

Supported interface orientations = Portrait
Supported interface orientations (iPad) = Portrait, PortraitUpsideDown, LandscapeLeft, LandscapeRight

If app delegate implemented application(_:supportedInterfaceOrientationsFor:), Info.plist maybe ignored. However, I didn't verify that.

Swift 4

func application(_ application: UIApplication,
                 supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    // Early return for iPad
    if UIDevice.current.userInterfaceIdiom == .pad {
        return [.all]
    }
    // Search for the visible view controller
    var vc = window?.rootViewController
    // Dig through tab bar and navigation, regardless their order 
    while (vc is UITabBarController) || (vc is UINavigationController) {
        if let c = vc as? UINavigationController {
            vc = c.topViewController
        } else if let c = vc as? UITabBarController {
            vc = c.selectedViewController
        }
    }
    // Look for model view controller
    while (vc?.presentedViewController) != nil {
        vc = vc!.presentedViewController
    }
    print("vc = " + (vc != nil ? String(describing: type(of: vc!)) : "nil"))
    // Final check if it's our target class.  Also make sure it isn't exiting.
    // Otherwise, system will mistakenly rotate the presentingViewController.
    if (vc is JTSImageViewController) && !(vc!.isBeingDismissed) {
        return [.allButUpsideDown]
    }
    return [.portrait]
}
John Pang
  • 2,403
  • 25
  • 25
  • it doesn't work for me... :( I want to change orientation only in one UINavigationController to landscapeRight, but it doesn't work :( – David Kadlcek Aug 03 '18 at 19:18
  • I don't know if it works for UINavigationController. It works for UIViewController subclass. You can use the print log to see what controller the system feed to you. Also there is restriction with the checkbox of orientation in project. Please have a look at that too. – John Pang Aug 07 '18 at 09:30
4

Your code in the desired VC will not work. I've managed this by adding the following code in the AppDelegate

var autoRotation: Bool = false

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
    return autoRotation ? .AllButUpsideDown : .Portrait
  }

And then you can make an helper class and add this method:

class func setAutoRotation(value: Bool) {
if let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate {
   appDelegate.autoRotation = value
}

}

Finally in your desired VC, you can call setAutoRotation(true) on didLoad and setAutoRotation(false) on willDissapear.

This can also achieved by subclassing the UINavigationController. You can find the answer here. Hope it helps

Community
  • 1
  • 1
glm4
  • 303
  • 1
  • 10