You want most screens to autorotate when the user turns the phone. But there can be some that just don’t work well in landscape (or in portrait). You want to prevent those few screens from autorotating.
Currently in iOS 8 and later systems you can control this, but only on the top-most view controller. So if you use a tab bar, or use navigation controllers, or any collection view type (page controller, etc), then the system controller for that collection view controller is asked about autorotation, but your dominant and selected view controller is not asked. Since we never write our code with nav controllers or tab bars this isn’t a problem right?
Snark aside, the description for the method supportedInterfaceOrientations says:
When the user changes the device orientation, the system calls this method on the root view controller or the topmost presented view controller that fills the window. If the view controller supports the new orientation, the window and view controller are rotated to the new orientation. This method is only called if the view controller’s shouldAutorotate method returns YES.
Of course the problem with this is that it violates the principle of putting the decision where the knowledge for that decision exists.
Here’s what I did to solve the problem. I created subclass of the UITabBarController (or whatever the top controller is) and added these following method overrides. (This code is in swift, but the Objective C code should be pretty obvious.)
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask { if let controller = selectedViewController { return controller.supportedInterfaceOrientations() } else { return return super.supportedInterfaceOrientations } }andoverride func shouldAutorotate() -> Bool { if let controller = visibleViewController { return controller.shouldAutorotate() } else { return super.shouldAutorotate() } }
This delegates the decision to the responsible view and removes the decision from the container, which shouldn’t have to know this.
I also filed a radar bug #23677037 “Controlling autorotation is not pushed to the visible view controller”.
What do these methods do?
The work together. The description I quoted above says that supportedInterfaceOrientation() is called if shouldAutorotate() returns true. But my testing shows that if shouldAutorotate() doesn’t exist, then supportedInterfaceOrientation() will do the job by itself. The iOS will tolerate one or the other missing and still work correctly it seems.
However, I plan to use both. My testing wasn’t exhaustive and docs say to use both. It’s easy to err on the side of caution in this case.