Tuesday, October 4, 2011

Orientations II

This series describes how to improve HelloWorld to support multiple iPhone orientations. In this post we explore Autoresizing.


Autoresizing
In Orientations I, we enabled auto-rotation for the HelloWorld UI in all orientations.  Now it is time to revisit the issues with the Landscape orientation.  Take another look:
The "Hello" button is off-screen, and what remains is off-center.  We could manually re-draw the UI for every orientation, and in a future article we shall do so.  In the meantime though, the app would be simpler if there were some way to configure HelloWorld to automatically "squeeze" and center the UI when in the Landscape position.  Fortunately for us, there is: autoresizing.

Autoresizing is a feature of the UIKit Framework which allows nested UIVIew(s) to automagically resize themselves when needed.  As described in Handling Layout Changes Automatically Using Autoresizing Rules, the size and position of embedded subviews will adjust to account for changes to their parent view. (The adjustments to the subviews trigger similar layout changes for their own child subviews, and so on.)  This setting is exposed in the code as the autoresizeSubviews property of the UIView, and is set to YES by default. When enabled, the selected view will automatically layout its subviews.

This property is also displayed visually in the Xcode IDE. To see, click on the MainStoryboard.storyboard file in the Project Navigator. Then, select "View" in the Document Outline (expand "Hello World View Controller" if necessary). Finally, open the Attribute Inspector for the view:
The Autoresize Subviews checkbox is circled in red.  As one can see, it is enabled for HelloWorld's main view.  So why doesn't the interface automatically adjust to fit the Landscape position?

Although autoresizing is enabled, it is not the entire story.  As Configuring Your Views to Support Multiple Orientations explains, when autoresizing of a parent view is enabled, the bounds of the child views are modified in accordance with their "autoresizing mask".  This is exposed in the code as the autoresizingMask property of the UIVIew, and takes values from the UIViewAutoresizing constants.

The autoresizing mask can also be manipulated visually in InterfaceBuilder.  To see, open the MainStoryboard.storyboard file again.  Then, select "Button" in the Document Outline  (expand "View" if necessary).  Finally, open the Size Inspector for the button:
The Autosizing box contains a visual representation of the current mask settings:
Solid red lines represent "struts" that fix the object to the boundary, while faint dashed lines represent "spings" that stretch as the boundary moves.  Click on them to toggle on and off.  Mouse over the Autosizing box to animate the Example box and see how the struts and springs affect autoresizing behavior.

For HelloWorld, we want the button to "float" up to the visible area when the phone is rotated.  Toggle off all the "struts" so that the Autosizing box looks like this:
Let's test our changes.  Build and run the app, and rotate the iPhone right to test the Landsape position:
Excellent.  The "Hello" button is visible again.  However, the textfield and label still retain their original fixed positions.  Open the Size Inspector for each and remove their Autosizing struts as well.  Build and run the app again, and rotate to Landscape position:
Awesome.  The textfield is still a little off-center though.  Beneath the Autosizing box there is a drop-down labelled Arrange:
Select the textfield and then choose "Center Vertically".  Just to be sure, center the label and button vertically as well.  Build and run the app again to test these changes.
Yay!  Now HelloWorld works beautifully in both Portrait and Landscape positions.  Build and deploy the app to your iPhone to confirm.