2

I am trying to build an app in Swift 5 which has a locked VC in potrait mode. In the next VC i want the orientation to support all orientations. I have tried several different solutions and none of them seem to work. I don't have Navigation Controller or TabBar. My application is now locked in potrait on both VC:s. I do support different device orientations in the general settings tab.

This is my current code in AppDelegate:

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

First VC:

override var shouldAutorotate: Bool {
    return false
}

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.portrait
}

override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
    return UIInterfaceOrientation.portrait
}

Second VC:

override var shouldAutorotate: Bool {
    return false
}

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return UIInterfaceOrientationMask.all
}
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
olle
  • 134
  • 3
  • 15
  • Use this link answer is available on first post-https://stackoverflow.com/questions/28938660/how-to-lock-orientation-of-one-view-controller-to-portrait-mode-only-in-swift. if again facing same issue, Let me know. – Ashutosh Mishra Jul 22 '20 at 12:29
  • try with separate UIWindow for both case. – SPatel Jul 29 '20 at 08:31

6 Answers6

4

This is unfortunately a tricky subject. But if you have no UINavigationController or UITabBarController it should be straightforward.

In the AppDelegate, like you've already done, put:

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

In the first UIViewController set:

// This stops the controller from rotating
override var shouldAutorotate: Bool {
  false
}

// This will rotate it back to portrait once it's presented again
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
  .portrait
}

In the second one you don't need anything because shouldAutorotate defaults to true.

NOTE: If you aren't using a UINavigationController you are probably presenting the second controller. The second controller has to have a modalPresentationStyle of either .fullScreen or .currentContext.

I tried it in a sample project and it's the only way I got it to work.

LorTos
  • 156
  • 3
2

1, First of all, enable all orientation in Info.plist.

2, Create protocol anywhere

protocol RotatableViewController {
    // No content necessary, marker protocol
}

3, Add this to Appdelegate:

extension AppDelegate {

    func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
        guard let rootVc = self.topViewControllerWithRootViewController(rootViewController: window?.rootViewController), rootVc.isBeingDismissed == false, let _ = rootVc as? RotatableViewController else { return .portrait } //swiftlint:disable:this all
        return .allButUpsideDown
    }

    private func topViewControllerWithRootViewController(rootViewController: UIViewController!) -> UIViewController? {
        if rootViewController == nil {
            return nil
        }
        if rootViewController.isKind(of: UITabBarController.self) {
            guard let root = rootViewController as? UITabBarController else { return UITabBarController() }
            return topViewControllerWithRootViewController(rootViewController: root.selectedViewController)
        } else if rootViewController.isKind(of: UINavigationController.self) {
            guard let root = rootViewController as? UINavigationController else { return UINavigationController() }
            return topViewControllerWithRootViewController(rootViewController: root.visibleViewController)
        } else if rootViewController.presentedViewController != nil {
            return topViewControllerWithRootViewController(rootViewController: rootViewController.presentedViewController)
        }
        return rootViewController
    }
}

4, Add RotatableViewController to your controllers, where you want to enable rotation.

class ViewController: UIViewController, RotatableViewController

Dris
  • 881
  • 8
  • 19
  • Make sure that "Requires full screen" is checked in Target Settings -> General -> Deployment Info. supportedInterfaceOrientationsFor delegate will not get called if that is not checked. – Dris Jul 22 '20 at 13:08
  • 1
    nice approach, but prefer naming `Rotatable` instead of `RotatableViewController` – Mohammad Reza Koohkan Aug 02 '20 at 11:42
0

Have you added the UISupportedInterfaceOrientations declare in your Info.plist?

You may turn it on in target config panel: device orientation

Next, remove application(_:supportedInterfaceOrientationsFor:) method in your AppDelegate. Just declare them in your view controllers.

BB9z
  • 2,432
  • 1
  • 30
  • 36
  • I tried to remove application(_:supportedInterfaceOrientationsFor:) and the device orientation is already checked as you proposed. It is still stuck in potrait mode for all VCs. – olle Jul 29 '20 at 11:03
  • @olle do you have a demo project? – BB9z Jul 30 '20 at 11:04
0

enter image description here enter image description here Step 1: Add below code in your project.

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")
        UINavigationController.attemptRotationToDeviceOrientation()
    }
    
}

Step 2: Add below code in AppDelegate class.

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

Step 3: Add below code in First view controller for lock potrait orientation.

override func viewWillAppear(_ animated: Bool) {
        AppUtility.lockOrientation(.portrait, andRotateTo: .portrait)
}
    
override func viewWillDisappear(_ animated: Bool) {
        //When controller Disappear unlock all orientation for other controllers.
        AppUtility.lockOrientation(.all)
}
0

you can add some utility like this

   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")
    UINavigationController.attemptRotationToDeviceOrientation()
}

and call it like this on view_willappear and view_willdisappear respectively

Utility.lockOrientation(.portrait, andRotateTo: .portrait)

in AppDelegate return the value of orientationlock in below delegate method

 var orientationLock = UIInterfaceOrientationMask.all

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
  return orientationLock 
}
nca
  • 397
  • 3
  • 12
0

For Swift 4, 5+

Just add this line on your ViewController:

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

For Portrait only:

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

For Landscape:

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

Or can set landscapeLeft, landscapeRight

Hope it will work for you.

Jamil Hasnine Tamim
  • 4,389
  • 27
  • 43