4

My App is basically a portrait only app. So nothing is rotating. But not there is ONE exception. The user can add photos and when viewing those photos full-screen, this ViewController SHOULD be allowed to rotate.

So I thought that if my ViewController that is presenting has supportedInterfaceOrientations return .portrait and also shouldAutorotate return false, that this should be enough, to prevent that one from rotation?!?

Turns out, that when I rotate while having the full-screen image presented, the one underneath is rotated as well.

To summarize:

RootViewController should NEVER rotate PresentedViewController can rotate, but his rotation should no rotate the RootViewController

Is there a way to achieve that?

Georg
  • 3,664
  • 3
  • 34
  • 75
  • A slightly hackish way is to force rotate back when closing the photo view controller. Look here (you need to set it to portrait, of course): https://stackoverflow.com/a/20987296/4543629 – LGP Apr 26 '18 at 13:01

4 Answers4

1

Probably too late, but in case if somebody will be faced with the same issue, i would provide my solution. Actually suppress rotation of underlying window is possible if set modalPresentationStyle = .fullScreen for presented controller, then if you take a look of "View UI Hierarchy" then you can notice that controller which represents controller in fullscreen will be removed from controllers hierarchy. But at the same time modalPresentationStyle = .overFullScreen keep everything as is, what causing rotating underlying controller even it set by default supported orientation to portrait, i.e. UIWindow who manages and routes system events over hierarchy respect settings of the toppest controller in case of modalPresentationStyle = .overFullScreen. So according to the facts, and if it is necessary to have e.g. custom presentation, i would suggest to use additional UIWindow which will be responsible for the presenting controller in fullscreen. i have implemented test project for the solution: here

Community
  • 1
  • 1
mks
  • 21
  • 3
0

You can give an exception like in AppDelegate:

//auto rotate
    func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
        //landscape for perticular view controller
        let navigationController = window?.rootViewController as? UINavigationController
        if let activeController = navigationController?.visibleViewController {
            if activeController.isKind(of: VGVideoVC.self)  {//Pass your VC here
//                print("I have found my controller!")
                return UIInterfaceOrientationMask.all;
            }else{
                return UIInterfaceOrientationMask.portrait;
            }
        }else{
            return UIInterfaceOrientationMask.portrait;
        }
    }

And in the Rest of the VC where you want it to be forcefully portrait, you can use like this:

//MARK:- Screen Orientation

override var supportedInterfaceOrientations: UIInterfaceOrientationMask{
    return .portrait
}

override var shouldAutorotate: Bool{
    return true
}

Hope this helps.

Abhirajsinh Thakore
  • 1,806
  • 2
  • 13
  • 23
0

I would say disable the orientation change for the complete app and listen to device orientation change in Photos view controller and update the UI of photosVC on device orientation change.

Something like this:

NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged), name:  Notification.Name("UIDeviceOrientationDidChangeNotification"), object: nil)

@objc func orientationChanged() {

if(UIDeviceOrientationIsLandscape(UIDevice.current.orientation)){

    print("landscape")
}

if(UIDeviceOrientationIsPortrait(UIDevice.current.orientation)){

    print("Portrait")
}

}

Be careful with the upside down and other orientations which you don't need.

Jasveer Singh
  • 354
  • 3
  • 10
0

Try this code below. I followed This tutorial and it works for me. What's going on is:

Step 1. Assuming inside General your Device Orientation is set to Portrait only:

Step 2. The code below that you add inside AppDelegate loops through the navigation controllers and then looks inside their top view controllers. If any of those vcs have a function with the name canRotate then that specific vc will change the device orientation from Step 1. by returning: return .allButUpsideDown

Add these 2 functions to the bottom of your AppDelegate:

// add this first function
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {

    // if the navigationController's root vc has a function inside of it named canRotate
    if let rootViewController = self.topViewControllerWithRootViewController(rootViewController: window?.rootViewController) {

        if (rootViewController.responds(to: Selector(("canRotate")))) {
            // Unlock landscape view orientations for this view controller
            return .allButUpsideDown;
        }
    }

    // Only allow portrait (standard behaviour). vcs that don't contain a function with the name "canRotate" can't rotate and stay in portrait only
    return .portrait;
}

// add this second function
// loop through tabBarController or any navigationControllers
private func topViewControllerWithRootViewController(rootViewController: UIViewController!) -> UIViewController? {
    if (rootViewController == nil) { return nil }
    if (rootViewController.isKind(of: UITabBarController.self)) {
        return topViewControllerWithRootViewController(rootViewController: (rootViewController as! UITabBarController).selectedViewController)
    } else if (rootViewController.isKind(of: UINavigationController.self)) {
        return topViewControllerWithRootViewController(rootViewController: (rootViewController as! UINavigationController).visibleViewController)
    } else if (rootViewController.presentedViewController != nil) {
        return topViewControllerWithRootViewController(rootViewController: rootViewController.presentedViewController)
    }
    return rootViewController
}

Step 3. Inside the modal vc you should add a function named: @objc func canRotate(){}. You don't have to call it anywhere or add anything inside it's curly braces. The code from Step 2 is looking for this function with the name canRotate. If the other vcs don't contain a function with that name then they can't rotate.

Inside the modal viewController that you want to rotate add the canRotate() function anywhere outside of viewDidLoad and inside viewWillDisappear add the code to set everything back to your regular Portrait only :

override func viewDidLoad() {
        super.viewDidLoad()
}

@objc func canRotate(){}

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

        // add this so once leaving this vc everything will go back to Portrait only
        if (self.isMovingFromParentViewController) {
            UIDevice.current.setValue(Int(UIInterfaceOrientation.portrait.rawValue), forKey: "orientation")
        }
}
Lance Samaria
  • 17,576
  • 18
  • 108
  • 256