Support different orientations in controllers embedded in a navigation

According to the documentation ("Interface Behaviors" section), if we need to support different orientations for the controllers in a navigation view, we should use "navigationControllerSupportedInterfaceOrientations" via delegation. Thus, we would be able to return the preferred orientation of each of the controllers by overriding, for example, preferredInterfaceOrientationForPresentation.


My problem is that this method never gets called. Any thoughts?


Context: I have a navigation controller with 2 controllers: A and B. A is portrait and B is landscape. When I push B from A, I need the screen to rotates automatically to landscape.

Replies

Isn't it:


     override open var supportedInterfaceOrientations : UIInterfaceOrientationMask     {
          return .all
     }

I already tried that. Where are you suggesting to put that? Did you reach any case where navigationControllerSupportedInterfaceOrientations got called?

Thanks

My problem is that this method never gets called. Any thoughts?


Please clarify which method do you mean by this method. "navigationControllerSupportedInterfaceOrientations" or preferredInterfaceOrientationForPresentation?


Assuming navigationControllerSupportedInterfaceOrientations, you may be mistaking something when implementing the delegate method `navigationControllerSupportedInterfaceOrientations` or setting the delegate of the navigation controller.


I created a simple project with navigation controller and navigationControllerSupportedInterfaceOrientations is called multiple times.

Please show your code.

Did you take care of the _ in the signature of the func before the argument ?

Yes, I did 🙂. Thanks for your answer 🙂

Yes, i´m talking about navigationControllerSupportedInterfaceOrientations. The delegate is fine, because the other methods are being called.

This is the signature:


func navigationControllerSupportedInterfaceOrientations(_ navigationController: UINavigationController) -> UIInterfaceOrientationMask


I'll try to push an example asap. Given the fact you already tried that, could you share your example with me?

Thank you very much for all your help!

Ramiro

OK, please show the code where you call this API.


And if possible show the complete code for the class, the problem may be elsewhere.

Please show your code first.

The delegate is fine, because the other methods are being called.


What other delegate method do you call successfully ?

I managed to get navigationControllerSupportedInterfaceOrientations called during the start up, but then, when I push a new controller, it´s not called (it seems that if you declare supportedInterfaceOrientations in the navigation controller, the delegate method is not called).



//Initial controller. PortaitViewController is the top view controller in CustomNavigationController. When I push LandscapeViewController, I need the device to rotates automatically to landscape.


class PortaitViewController: UIViewController {

    @IBAction func landscapeTUI(_ sender: Any) {
        let vc = LandscapeViewController(nibName: "LandscapeViewController", bundle: nil)
        self.navigationController?.pushViewController(vc, animated: true)
    }

    override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
        return .portrait
    }

    override var shouldAutorotate : Bool {
        return true
    }
}
class LandscapeViewController: UIViewController {


    override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
        return .landscapeLeft
    }


    override var shouldAutorotate : Bool {
        return true
    }
  
  override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
        return .landscapeLeft
    }


}


class CustomNavigationController: UINavigationController {

    override func viewDidLoad() {
        super.viewDidLoad()
        self.delegate = self
    }


}

extension CustomNavigationController: UINavigationControllerDelegate {

    func navigationControllerSupportedInterfaceOrientations(_ navigationController: UINavigationController) -> UIInterfaceOrientationMask {
        return self.topViewController?.supportedInterfaceOrientations ?? .portrait
    }


}

shouldAutorotate is true
if the content should rotate, otherwise
false
. This method returns
true
by default.

So, the following is not needed

    override var shouldAutorotate : Bool {
        return true
    }



Don't you confuse what shouldAutorotate is for ?

It does not force to rotate, it allows to automatically rotate the view when device is turned


have a look here on how to handle the case of a navigationController.

https://stackoverflow.com/questions/27037839/force-landscape-mode-in-one-viewcontroller-using-swift

Hi! Thanks again!

Yes, I understand the purpose of shouldAutorotate.


Don´t you feel like the way tried in that post it´s like a hack? Also I´ve experienced weird animations with that approach.


let value = UIInterfaceOrientation.landscapeLeft.rawValue 
UIDevice.current.setValue(value, forKey: "orientation")

Honestly, I did not test.


But I know that you need specific handling of some functions to accomodate the NavigationController that is inserted in the flow of controllers.

So the following did appear to make sense:


// If your view is embedded in a navigation controller the above alone won't work. you have to cascade up // So add the following extension after the class definition

extension UINavigationController {

override open var shouldAutorotate: Bool {
    get {
        if let visibleVC = visibleViewController {
            return visibleVC.shouldAutorotate
        }
        return super.shouldAutorotate
    }
}

override open var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation{
    get {
        if let visibleVC = visibleViewController {
            return visibleVC.preferredInterfaceOrientationForPresentation
        }
        return super.preferredInterfaceOrientationForPresentation
    }
}

override open var supportedInterfaceOrientations: UIInterfaceOrientationMask{
    get {
        if let visibleVC = visibleViewController {
            return visibleVC.supportedInterfaceOrientations
        }
        return super.supportedInterfaceOrientations
    }
}}

Thanks anyways. The approach mentioned above is essentially what I did, and does not work.

Last idea. Did you try to set navController and child controllers to full screen presentation instead of automatic ?