Rotating to Show Different iOS Views
In the tradition of explaining fairly simple things at great length this approach is based on the documentation from Apple describing Creating an Alternate Landscape Interface and various answers on stackoverflow. It differs from the information I could find on the internet because the flow includes a navigation controller and uses a storyboard. The flow between the screens in the interface is shown in the diagram below.
There is a first screen, A, that has two alternate views, one portrait and one landscape. These can’t be created using the springs and struts layout tools. Rotating the phone while screen A is showing should switch between the two views. Tapping some element on screen A, in either view, transitions to screen B. Screen B handles rotation itself by redrawing its interface in drawRect:. Going back from B should show the correct version of A. There’s a navigation controller wrapping the whole interface that provides the navigation bar and button in view B to go back to view A.
The implementation uses the navigation controller’s pushViewController:animated: and popViewControllerAnimated: methods to display the portrait and landscape views. There is a single parent class, AViewController, containing common functionality, with two subclasses, APortraitViewController and ALandscapeViewController. XCode can be used to layout the different portrait and landscape views. The initial state is in portrait orientation (applications always start in portrait orientation and are sent rotation messages if required) so the root view controller for the navigation controller is APortraitViewController. When the orientation changes to landscape ALandscapeViewController is pushed and when it changes back to portrait ALandscapeViewController is popped. APortrait is never removed it’s just hidden by ALandscape.
Before looking at the code to push and pop the views we have to consider how to deal with view B, and what the storyboard looks like. View B presents some problems because you can get get to view B from the landscape version of view A. While showing view B you can rotate to portrait, and press the back button, at which point you need to go back to A. At this point you want the APortrait view shown. This is the path, 1, 2, 3, 4 in the diagram below.
The solution to this is quite simple. At appropriate times, to be described below, check whether the portrait view is being show in landscape orientation, or the landscape view in portrait orientation and perform the appropriate push or pop.
Finally, the last thing before the code, the picture below shows how the storyboard laid out.
The ALandscape view controller is not connected to APortrait. It’s given an identifier that’s used to load it from code. Both APortrait and ALandscape use push segues to go to B.
Here’s the code that swaps the portrait and landscape versions of the A view controller. It’s in the APortraitViewController class, because an instance of that class is always in memory, so self refers to APortraitViewController. Instances of the ALandscapeViewController are created as needed for pushing and then destroyed when they are popped. It’s important to set the model and any other properties for the landscape instance each time one is created.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
There are two occasions when swapControllersIfNeeded must be called. First, when the orientation changes, which will handle the case where ALandscape or APortrait is showing and must be swapped. Second, when the navigation view controller shows a new view controller. This is to take care of the case where rotation takes place while view B is showing.
To deal with orientation changes register, in awakeFromNib, for orientation change events and implement a method handle them.
1 2 3 4 5 6 7 8 9 10 11 12
For the navigation controller case APortraitViewController must conform to the UINavigationControllerDelegate protocol and implement one of its methods.
1 2 3 4 5 6 7 8 9 10 11 12
One final wrinkle is that we don’t want a back button appearing at the top of the landscape view pointing back to the portrait view. To prevent this the Shows Navigation Bar property for the Navigation Controller must be unchecked. However, we do want a back button to go from view B back to view A so in the viewWillAppear: method of the B view controller the navigation bar is unhidden. If you want APortrait and ALandscape views can have title bars added to them so their appearance is as it would be if Shows Navigation Bar was checked.
1 2 3 4 5