Felipe Peña
Felipe Peña

Reputation: 2850

How to keep controller in only one orientation?

I have two classes which subclass from UIViewController. PortraitViewController and LandscapeViewController.

These classes have their vars shouldAutorotate, supportedInterfaceOrientations and preferredInterfaceOrientationForPresentation with override implementation so they can stick to portrait or landscape orientation accordingly.

My application accepts Portrait and Landscape mode.

Now, I also have:

  1. ViewController1 which subclasses from PortraitViewController
  2. ViewController2 which subclasses from LandscapeViewController

When I show ViewController1 with a UINavigationController attached to it, it sticks with portrait as expected.

When I show ViewController2 with a UINavigationController attached to it, as a modal nav on top of ViewController1, it sticks with landscape as expected.

There's an extension for UINavigationController which also overrides the vars mentioned above, but it reads properties from the visibleController parameter.

But when I dismiss ViewController2, ViewController1 appears in landscape.

How can I make ViewController1 stick in portrait mode, regardless of what I show on top of it?

Note: Every time the device is in portrait mode.

EDIT:

Here's a demo project: https://www.dropbox.com/s/ishe88e1x81nzlp/DemoOrientationApp.zip?dl=0

Upvotes: 0

Views: 68

Answers (2)

matt
matt

Reputation: 534893

Okay, so:

Throw away your UINavigationControllerExtension.

Change your Info.plist so that we launch only into portrait:

enter image description here

(Delete items 1 and 2.)

In the app delegate, add this code, to allow us to rotate to landscape after launch:

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

In ViewController, fix the code as follows:

class ViewController: PortraitViewController, UINavigationControllerDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        self.navigationController?.delegate = self
    }
    func navigationControllerSupportedInterfaceOrientations(_ navigationController: UINavigationController) -> UIInterfaceOrientationMask {
        return self.supportedInterfaceOrientations
    }
    @IBAction func showView(_ sender: Any) {
        let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController2") as! ViewController2
        let nav = UINavigationController(rootViewController: vc)
        nav.delegate = vc
        present(nav, animated: true, completion: nil)
    }
}

In ViewController2, fix the code as follows:

class ViewController2: LandscapeViewController, UINavigationControllerDelegate {
    func navigationControllerSupportedInterfaceOrientations(_ navigationController: UINavigationController) -> UIInterfaceOrientationMask {
        return self.supportedInterfaceOrientations
    }
    func navigationControllerPreferredInterfaceOrientationForPresentation(_ navigationController: UINavigationController) -> UIInterfaceOrientation {
        return self.preferredInterfaceOrientationForPresentation
    }
    @IBAction func dismiss(_ sender: Any) {
        dismiss(animated: true, completion: nil)
    }
}

The app now behaves as desired.

Upvotes: 0

Ken Boreham
Ken Boreham

Reputation: 904

The visibleViewController is either the top view controller of the navigation stack or a view controller presented from the navigation controller.

So, when you present the second navigation controller, the first navigation controller reads the second navigation controller property which passes back the landscape view controller's property.

What you need to use instead is topViewController. That way, the setting is limited to the view controller stack.

Upvotes: 1

Related Questions