How to lock orientation of one view controller to portrait mode only in Swift

后端 未结 16 2056
梦谈多话
梦谈多话 2020-11-22 12:07

Since my app got support for all orientation. I would like to lock only portrait mode to specific UIViewController.

e.g. assume it was Tabbed Application and when Si

相关标签:
16条回答
  • 2020-11-22 12:27

    Add this code to force portrait and lock it:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        // Force the device in portrait mode when the view controller gets loaded
        UIDevice.currentDevice().setValue(UIInterfaceOrientation.Portrait.rawValue, forKey: "orientation") 
    }
    
    override func shouldAutorotate() -> Bool {
        // Lock autorotate
        return false
    }
    
    override func supportedInterfaceOrientations() -> Int {
    
        // Only allow Portrait
        return Int(UIInterfaceOrientationMask.Portrait.rawValue)
    }
    
    override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
    
        // Only allow Portrait
        return UIInterfaceOrientation.Portrait
    }
    

    In your AppDelegate - set supportedInterfaceOrientationsForWindow to whatever orientations you want the entire application to support:

    func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
        return UIInterfaceOrientationMask.All
    } 
    
    0 讨论(0)
  • 2020-11-22 12:27

    As of iOS 10 and 11, iPad supports Slide Over and Split View. To enable an app in Slide Over and Split View, Requires full screen must be unchecked. That means the accepted answer cannot be used if the app wants to support Slide Over and Split View. See more from Apple's Adopting Multitasking Enhancements on iPad here.

    I have a solution that allows (1) unchecking Requires full screen, (2) just one function to be implemented in appDelegate (especially if you don't want to / can't modify the target view controllers), and (3) avoid recursive calls. No need of helper class nor extensions.

    appDelegate.swift (Swift 4)

    func application(_ application: UIApplication,
                     supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
        // 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 TargetViewController) && !(vc!.isBeingDismissed) {
            return [.portrait]
        }
        return [.all]
    }
    

    Edit

    @bmjohns pointed out that this function is not called on iPad. I verified and yes it was not called. So, I did a bit more testing and found out some facts:

    1. I unchecked Requires full screen because I want to enable Slide Over and Slide View on iPad. That requires the app to support all 4 orientation for iPad, in Info.plist: Supported interface orientations (iPad).

    My app works same way as Facebook: on iPhone, most of the time it is locked to portrait. When viewing image in full screen, allows users to rotate landscape for better view. On iPad, users can rotate to any orientation in any view controllers. So, the app looks nice when iPad is stand on Smart Cover (landscape left).

    1. For iPad to call application(_:supportedInterfaceOrientationsFor), in Info.plist, only keep portrait for iPad. The app will lose Slide Over + Split View ability. But you can lock or unlock the orientation for any view controller, in just one place and no need to modify ViewController class.

    2. Finally, this function get called on view controller's life cycle, when view is displayed/removed. If your app need to lock/unlock/change orientation in other time, it might not work

    0 讨论(0)
  • 2020-11-22 12:28

    Best Solution for lock and change orientation on portrait and landscape:

    Watch this video on YouTube:

    https://m.youtube.com/watch?v=4vRrHdBowyo

    This tutorial is best and simple.

    or use below code:

    See this picture

    // 1- in second viewcontroller we set landscapeleft and in first viewcontroller we set portrat:

    // 2- if you use NavigationController, you should add extension

    import UIKit
    
    class SecondViewController: UIViewController {
    
    
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation")
        }
    
        override open var shouldAutorotate: Bool {
            return false
        }
    
        override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
            return .landscapeLeft
        }
    
        override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
            return .landscapeLeft
        }
        
        
    
        override func viewDidLoad() {
            super.viewDidLoad()
        }
    
    //write The rest of your code in here
    
    
    }
    
    //if you use NavigationController, you should add this extension
    
    extension UINavigationController {
        override open var supportedInterfaceOrientations: UIInterfaceOrientationMask {
            return topViewController?.supportedInterfaceOrientations ?? .allButUpsideDown
        
        }
    }
    
    0 讨论(0)
  • 2020-11-22 12:32

    To set Landscape orientation to all view of your app & allow only one view to All orientations (to be able to add camera roll for example):

    In AppDelegate.swift:

    var adaptOrientation = false
    

    In: didFinishLaunchingWithOptions

    NSNotificationCenter.defaultCenter().addObserver(self, selector: "adaptOrientationAction:", name:"adaptOrientationAction", object: nil)
    

    Elsewhere in AppDelegate.swift:

    func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {
        return checkOrientation(self.window?.rootViewController)
    }
    
    func checkOrientation(viewController:UIViewController?)-> Int{
        if (adaptOrientation == false){
            return Int(UIInterfaceOrientationMask.Landscape.rawValue)
        }else {
            return Int(UIInterfaceOrientationMask.All.rawValue)
        }
    }
    
    func adaptOrientationAction(notification: NSNotification){
        if adaptOrientation == false {
            adaptOrientation = true
        }else {
            adaptOrientation = false
        }
    }
    

    Then in the view that segue to the one you want to be able to have All orientations:

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
        if (segue.identifier == "YOURSEGUE") {
            NSNotificationCenter.defaultCenter().postNotificationName("adaptOrientationAction", object: nil)
        }
    }
    
    override func viewWillAppear(animated: Bool) {
        if adaptOrientation == true {
            NSNotificationCenter.defaultCenter().postNotificationName("adaptOrientationAction", object: nil)
        }
    }
    

    Last thing is to tick Device orientation: - Portrait - Landscape Left - Landscape Right

    0 讨论(0)
  • Actual tested Solution for this.In my example I need my whole app should be in portrait mode, but only one screen's orientation should be in landscape mode.

    Code in AppDelegate as above answers described.

    var orientationLock = UIInterfaceOrientationMask.all
    
    func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask 
    {
      return self.orientationLock
    }
    struct AppUtility {  
    
    static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {
        if let delegate = UIApplication.shared.delegate as? AppDelegate {
            delegate.orientationLock = orientation
        }
    }
    static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) {
    self.lockOrientation(orientation)     
    UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
    }  
    }
    

    Then write down this code before your landscape orientation viewcontroller will be presented/push.

    override func viewWillAppear(_ animated: Bool) {  
    super.viewWillAppear(animated)
    AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait)
    }  
    

    Then write down this code in actual viewcontroller(For landscape view)

    override func viewWillAppear(_ animated: Bool) {  
    super.viewWillAppear(animated)
    AppDelegate.AppUtility.lockOrientation(UIInterfaceOrientationMask.landscape, andRotateTo: UIInterfaceOrientation.landscape)
    }  
    
    0 讨论(0)
  • 2020-11-22 12:39

    A bunch of great answers in this thread, but none quite matched my needs. I have a tabbed app with navigation controllers in each tab, and one view needed to rotate, while the others needed to be locked in portrait. The navigation controller wasn't resizing it's subviews properly, for some reason. Found a solution (in Swift 3) by combining with this answer, and the layout issues disappeared. Create the struct as suggest by @bmjohns:

    import UIKit
    
    struct OrientationLock {
    
        static func lock(to orientation: UIInterfaceOrientationMask) {
            if let delegate = UIApplication.shared.delegate as? AppDelegate {
                delegate.orientationLock = orientation
            }
        }
    
        static func lock(to orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation) {
            self.lock(to: orientation)
            UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
        }
    } 
    

    Then subclass UITabBarController:

        import UIKit
    
    class TabBarController: UITabBarController, UITabBarControllerDelegate {
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            self.delegate = self
        }
    
        func tabBarControllerSupportedInterfaceOrientations(_ tabBarController: UITabBarController) -> UIInterfaceOrientationMask {
            if tabBarController.selectedViewController is MyViewControllerNotInANavigationControllerThatShouldRotate {
                return .allButUpsideDown
            } else if let navController = tabBarController.selectedViewController as? UINavigationController, navController.topViewController is MyViewControllerInANavControllerThatShouldRotate {
                return .allButUpsideDown
            } else {
                //Lock view that should not be able to rotate
                return .portrait
            }
        }
    
        func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
            if viewController is MyViewControllerNotInANavigationControllerThatShouldRotate {
                OrientationLock.lock(to: .allButUpsideDown)
            } else if let navController = viewController as? UINavigationController, navController.topViewController is MyViewControllerInANavigationControllerThatShouldRotate {
                OrientationLock.lock(to: .allButUpsideDown)
            } else {
                //Lock orientation and rotate to desired orientation
                OrientationLock.lock(to: .portrait, andRotateTo: .portrait)
            }
            return true
        }
    }
    

    Don't forget to change the class of the TabBarController in the storyboard to the newly created subclass.

    0 讨论(0)
提交回复
热议问题