how to lock portrait orientation for only main view using swift

Solution 1:

According to the Swift Apple Docs for supportedInterfaceOrientations:

Discussion

When the user changes the device orientation, the system calls this method on the root view controller or the topmost presented view controller that fills the window. If the view controller supports the new orientation, the window and view controller are rotated to the new orientation. This method is only called if the view controller's shouldAutorotate method returns true.

Your navigation controller should override shouldAutorotate and supportedInterfaceOrientations as shown below. I did this in a UINavigationController extension for ease:

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

    public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
        return (visibleViewController?.supportedInterfaceOrientations())!
    }
}

And your main viewcontroller (portrait at all times), should have:

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

Then, in your subviewcontrollers that you want to support portrait or landscape:

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

Edit: Updated for iOS 9 :-)

Solution 2:

Also answered [here]

Things can get quite messy when you have a complicated view hierarchy, like having multiple navigation controllers and/or tab view controllers.

This implementation puts it on the individual view controllers to set when they would like to lock orientations, instead of relying on the App Delegate to find them by iterating through subviews or relying on inheritance.

Swift 3

In AppDelegate:

/// set orientations you want to be allowed in this property by default
var orientationLock = UIInterfaceOrientationMask.all

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

In some other global struct or helper class, here I created AppUtility:

struct AppUtility {

    static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {

        if let delegate = UIApplication.shared.delegate as? AppDelegate {
            delegate.orientationLock = orientation
        }
    }

    /// OPTIONAL Added method to adjust lock and rotate to the desired orientation
    static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) {

        self.lockOrientation(orientation)

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

}

Then in the desired ViewController you want to lock orientations:

 override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    AppUtility.lockOrientation(.portrait)
    // Or to rotate and lock
    // AppUtility.lockOrientation(.portrait, andRotateTo: .portrait)

}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    // Don't forget to reset when view is being removed
    AppUtility.lockOrientation(.all)
}

Solution 3:

If your view is embedded in navigationcontroller in storyboard set the navigation controller delegate UINavigationControllerDelegate and add the following method

class ViewController: UIViewController, UINavigationControllerDelegate {
    func navigationControllerSupportedInterfaceOrientations(navigationController: UINavigationController) -> UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.Portrait
    }
}

Solution 4:

Update: if you're having trouble to set orientation right after the app launches in iOS 10, try do it in ObjC instead of Swift, and with class MyNavigationController: MyNavigationControllerBase:

@implementation ABNavigationControllerBase
- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}
@end

Swift 3:

class MyNavigationController: UINavigationController {
    override func viewDidLoad() {
        super.viewDidLoad()
        UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
    }
    override var shouldAutorotate: Bool {
        return false
    }
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .portrait
    }
    override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
        return .portrait
    }
}

Solution 5:

Same JasonJasonJason answer in Swift 4.2+ (It worked correctly with iOS 11)

1- Override shouldAutorotate and supportedInterfaceOrientations as shown below.

extension UINavigationController {

open override var shouldAutorotate: Bool {
    return true
}

open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return (visibleViewController?.supportedInterfaceOrientations)!
    }
}

2- And your main viewcontroller (portrait at all times), should have:

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

3- Then, in your subviewcontrollers that you want to support portrait or landscape:

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