How to Rock and Roll Your Apps
I mean, how to rock and rotate interfaces in your UIKit based apps.
Apple taught me most I need to know about how to manage the interface orientation, yet still left some dark corners that I had to explore by myself. Now that I’ve seen the whole landscape, I want to share it with you.
Read the documentation first if you haven’t yet. After that, you should know how to write the rotation code to respond to orientation changes, if only you are notified of the orientation changes in the first place.
Your view controller is always notified of the orientation changes if it is the frontmost view controller – the only view controller, the top modal view controller, the top view controller in the top navigation controller, or the current selected view controller in top tab bar controller. In these cases, simply following the documentation by implementing your rotation logic in those view rotation methods1 is enough.
However, no view rotation message will be sent to your view controller if the rotation occurs when it is not in the frontmost. Well, it is very reasonable behavior though, since its view is invisible then. Laziness is a great virtue here. What’s the point of rotating the invisible view under the hood except for consuming more battery, especially considering that the interface may be rotated back to its original orientation? What makes it a problem is the fact that these messages will neither be sent to your view controller when it becomes the frontmost one later. Your view controller will be never aware of the orientation changes that happen when it is not in the frontmost! One example for better understanding:
- A is the current selected view controller in a tab bar controller and the device is in portrait orientation.
- View controller B is selected in the tab bar controller.
- The device is rotated to landscape orientation.
- A is selected again.
See the problem? The orientation is portrait at first, and view controller A presents its view correctly; the orientation becomes landscape later, but A does not know that and still presents a portrait view when it returns to the frontmost.
In fact, the example could be reduced to a simpler one:
- B is the current selected view controller in a tab bar controller and the device is in portrait orientation.
- The device is rotated to landscape orientation.
- View controller A is selected.
The example can be easily extended to view controllers managed by a navigation controller.2 The problem is reduced to: view controller A initially sets up its view in portrait mode and relies on the code in view rotation methods to adjust the view to new orientations, but view rotation messages are never sent to A so it never rotates its view.
It is clear now that not only view rotation methods should have code for adjusting view for different orientations, but also the initial view setting up code should be orientation dependent. And that’s Apple didn’t tell us.
Finally, here is my way to attack this problem:
- Factor the orientation-dependent layout code into a method, naturally called
(void)layoutView:(UIInterfaceOrientation)orientation
. - Call
layoutView:
in bothviewWillAppear:
andwillAnimateRotationToInterfaceOrientation:duration:
.
May this article help you rotate your apps nicely and may your apps rock.
-
willRotateToInterfaceOrientation:duration:
,willAnimateRotationToInterfaceOrientation:duration:
, anddidRotateFromInterfaceOrientation:
. ↩ -
The situation of UISplitViewController with modal view controller is different. The rotation messages will be properly sent to UISplitViewController when its modal view controller is dismissed. I hope Apple do this for all view controllers. ↩