Do you have an idea for an app but lack the programming knowledge to begin building it? In this weekly blog series, I will take you, the non-programmer, step by step through the process of creating apps for the iPhone, iPod touch, and iPad. Join me each week on this adventure, and you will experience how much fun turning your ideas into reality can be! This is Part 25 of the series. If you are just getting started, check out the beginning of the series here.
One of the main requirements for iOS developers is to create apps that can adapt to different user interface sizes. With the 3.5-inch and 4-inch iPhones, the iPad, and rumors of new Apple devices with larger and smaller form factors, you need to make sure your app can adapt and look great on all these devices.
Apple's main tool for creating adaptive user interface is Auto Layout introduced in Xcode 4.5 and enhanced slightly in Xcode 4.6. The main building block used in Auto Layout is called a constraint. A constraint describes rules for the layout of user-interface objects. For example, you can create a constraint that specifies the width of an element, or the spacing between multiple elements.
Auto Layout in Xcode 4.x
Auto Layout in Xcode 4.x was difficult to understand and use. In its first incarnation, Auto Layout automatically added constraints as soon as you created a new scene as well as each time you dropped a new user interface object on the scene. There were certain constraints you couldn't delete and often it was a mystery why specifying specific constraints didn't produce the desired result. Based on these difficulties, many developers abandoned Auto Layout and went back to the older "springs and struts" technology.
Auto Layout in Xcode 5
Fortunately, as you move your apps to Xcode 5, you will quickly see that Auto Layout is far easier to tame than in previous versions of Xcode. Apple has greatly enhanced Auto Layout so it is far more intuitive to use, and doesn't get in your way. In fact, when you add a new scene to a storyboard, by default, there are no Auto Layout constraints automatically added to the scene. This gives you complete control over when constraints are added to a scene!
As I demonstrated in my previous post, when you move a scene that contains Auto Layout constraints from Xcode 4.6 to Xcode 5, the existing constraints are automatically moved forward for you. However, sometimes you want to start with a blank slate, so there is an option in Xcode to completely remove all existing constraints from a scene. This is where I left off in my previous post.
In this post I'm going to add the necessary constraints to the Write Review scene in iAppsReview to get it to adapt to different screen sizes and orientations. If you would like to follow along, you can get the latest version of the iAppsReview project from this link. If you get stuck along the way, you can get the completed version of the project at this link.
Adapting to Landscape Orientation
In my previous post, I removed all Auto Layout constraints from the Write Review scene and then I repositioned and resized some of the UI controls to make it more aesthetically pleasing. When I ran the app in the Simulator it looked great in portrait orientation as shown in the image on the left in Figure 1, but when I rotated it to horizontal orientation, it looked pretty bad, as shown in the image on the right in Figure 1. The table view at the top of the scene looks fine, but the other controls are pushed to the upper-left side of the scene directly below the App Category table view row. That's because Interface Builder fixes the user-interface elements' width, height and position relative to the top-left corner of the parent view.
|Figure 1 - The Write Review scene in portrait and landscape orientations.|
Adding Constraints to the Scene
At this point, we could manually create an Auto Layout constraint for each of these controls so that they reposition and resize to take full advantage of the extra screen width. However, Xcode 5 offers an option where it automatically adds the constraints it thinks are best to the scene. Let's try that first.
- Open the iAppsReview project in Xcode.
- Select the Mainstoryboard file in the Project Navigator.
- Click the status bar at the top of the Write Review scene to select it.
- In the bottom-right corner of the Interface Builder panel, is the Auto Layout menu as shown in Figure 2 (it looks more like a toolbar to me, but Apple is calling it a menu so I'll go with that.)
|Figure 2 - The Auto Layout menu|
Here is a brief explanation of each button:
- Toggle 3.5 / 4-inch - Toggles between the iPhone 3.5-inch and 4-inch form factors.
- Align - Allows you to align the edges and centers of user interface objects. The options in this button can only be chosen if you have multiple user-interface controls selected in the design surface.
- Pin - Allows you to specify the height, width, and relative spacing between controls.
- Resolve Auto Layout Issues - Allows you to update, add, reset, and clear constraints.
- Resizing Behavior - Allows you to specify if resizing views applies to Siblings and Ancestors or Descendants.
- In the Auto Layout menu, click the Resolve Auto Layout Issues button and select Reset to Suggested Constraints in Write Review View Controller from the popup menu (Figure 3).
|Figure 3 - Resetting constraints|
- Now let's take a look at the constraints that Xcode has created for this view controller. Go to the Document Outline (if it's not visible, you can select Editor > Show Document Outline from the Xcode menu) and expand the Write Review View Controller scene as shown in Figure 4.
|Figure 4 - No constraints were added!|
Xcode lists all constraints for a particular view controller within the Document Outline, but as you can see, there are no constraints. What happened?
Notice that the star rating, text field, text view, and other controls are nested inside of a View which is in turn nested inside of the Table View. Table views don't need constraints in order to resize themselves—it happens automatically. Therefore, Xcode saw the table view and decided nothing else needed to be done (an incorrect assumption, which makes this a bug!)
- In order to get Xcode to add constraints to the user-interface controls in the embedded view, select the View object in the Document Outline and then go back the Auto Layout menu, click the Resolve Auto Layout Issues button and select Reset to Suggested Constraints in Write Review View Controller again. When you do this, Xcode adds the constraints shown in Figure 5 to the view controller.
|Figure 5 - Constraints in the Document Outline|
Examining the Constraints
Let's take a closer look at the constraints that Xcode created for us.
- Xcode displays constraints that apply to a specifc user-interface object nested below that object in the Document Outline. For example, below Button - Post is a Height constraint of 34 points; and below the Image View - AddImage.png object, there are Height and Weight constraints.
- All constraints that apply to multiple objects are listed one level up, below the View object. For example, the first constraint is listed as Horizontal Space - Mm Star Rating - View. Click on this constraint and you will see a visual representation of the constraint highlighted in the scene as shown in Figure 6.
|Figure 6 - The star rating control's Horizontal Space constraint|
This highlighted constraint specifies the horizontal space between the star rating control and the edge of the view. Notice that clicking on this constraint also displays all other constraints related to the star rating control:
- The Vertical Space between the top of the view and the top of the star rating control
- The Horizontal Space between the star rating control and the image view control
- The Vertical Space between the star rating control and the text view
- The Leading Alignment of the star rating control and the text view
- If you click on a constraint in Interface Builder, it highlights the corresponding constraint in the Document Outline. I recommend clicking on some of the other constraints in the Document Outline to see what Xcode has automatically added for you.
- There's one other thing we should look at in the Document Outline. Notice the warning icon to the right of the Write Review Scene node as shown in Figure 7. This lets you know that there is a problem with the constraints in this scene—which is odd given that Xcode created the constraints for us!
|Figure 7 - A Document Outline constraint warning|
- Click the warning icon. When you do this, the Document Outline slides to left and is replaced by the Auto Layout error detail panel shown in Figure 8.
|Figure 8 - Constraint warning detail|
The error detail panel tells us that the expected width of the Post button is 321, but its actual width is 320. Let's take a closer look at the Post button's constraints so we can see if something is wrong.
- At the top-left corner of the error detail panel (Figure 8), click the left arrow ( < ) to go back to the Document Outline.
- In the Document Outline, click the Button - Post node to select it. Next, go to the Sizing Inspector (the second button from the right in the Inspector toolbar) and you should see the constraints shown in Figure 9. For future reference, this is the easiest way to see all of the constraints that apply to a particular user-interface object.
|Figure 9 - Constraints in the Size Inspector|
There is really nothing here that looks suspicious. At the top of the Size Inspector, you can see the width of the button is 320 points, which is exactly what it should be for the iPhone. If you click the gear on the right side of the Leading Space to: constraint and then choose Select and Edit... from the popup menu, you will be taken to the Attributes Inspector and you will see the constraint detail shown in Figure 10.
|Figure 10 - A constraint in the Attributes Inspector|
The Horizontal Space Constraint shows there are zero (0) points between the parent view and the Post button, which is as expected since we expanded the button the full width of the view. It seems this warning is just a bug in Auto Layout. Let's see if we can get rid of the warning by having Xcode fix the "problem" for us.
- In the Document Outline panel, click the warning icon again that is located to the right of the Write Review Scene node.
- In the constraint error detail panel, click the icon to the right of the Post button warning. This displays the popup shown in Figure 11.
|Figure 11 - Options for fixing the constraint|
This popup offers three possible fixes:
- Update Frame - Changes the user-interface object's frame (location, height, width) to match the constraints.
- Update Constraints - Changes the constraints to match the user-interface object's frame.
- Reset to Suggested Constraints - Removes each constraint attached to the user-interface object and adds suggested constraints based on the frame.
- Based on the fact that the Post button's frame is fine the way that it is, I would prefer to choose Update Constraints to change the constraints to match the button's frame. Unfortunately, selecting this option doesn't do anything. Go ahead and select the Update Frame option and then click the Fix Misplacement button. When you do this, the message No Auto Layout Issues is displayed in the panel as shown in Figure 12.
|Figure 12 - No Auto Layout issues!|
- Click the back arrow at the top of the constraint warning detail panel to go back to the Document Outline. Click the Button - Post node again and then go back to the Size Inspector. You will see that the Width of the Post button is now set to 321. This satisifies the Auto Layout engine and won't cause any problems at run time, so we're good to go.
Testing the Constraints
Now we're ready to test how the new constraints work at run time.
- Make sure iPhone Retina 3.5-inch is selected in the Scheme control at the top-left corner of the Xcode window.
- Click the Run button. When the app appears in the Simulator, select the Write a Review option.
- If the Simulator isn't already rotated to landscape orientation, select Hardware > Rotate Right from the Simulator menu. The user-interface controls should be resized and repositioned as shown in the image on the left in Figure 13.
|Figure 13 - The user interface controls resize and reposition in landscape orientation!|
- Scroll down in the Simulator (you can click and drag up) and you should see the text view and Post button as shown in the image on the right side of Figure 13.
Everything looks good! So, aside from the small bug with the Post button, Auto Layout did a decent job of adding the correct constraints to the Write Review scene so that controls adapted properly to landscape orientation.
Tweaking the Constraints
Although I'm mostly happy with the constraints that were automatically generated by Xcode, there is one thing I'd like to change. If you look at the image on the left in Figure 13, the star rating control has expanded to fill the available space, making the stars a bit larger. I would prefer that this control maintain its size. To make this happen, it makes sense to add a Width constraint to the star rating control. Let's give that a try.
- Go back to Xcode and click the Stop button.
- In the Write Review scene, click on the star rating control to select it.
- In the Auto Layout menu at the bottom of the Interface Builder window, click the Pin button (Figure 14).
|Figure 14 - Pin the Width|
- In the Add New Constraints popup, select the Width check box and then click the Add 1 Constraint button.
- With the star rating control still selected, go to the Size Inspector and you can see the newly added constraint (Figure 15).
|Figure 15 - The new Width constraint|
- Let's see how it looks at run time. Click the Run button and when the app appears in the Simulator, select the Write a Review option. If the Simulator is not in landscape orientation, select Hardware > Rotate Right from the Simulator menu.
- You may be surprised to see that the star rating control is still expanded. Our Width constraint didn't do the trick! Fortunately, Xcode is here to help. If you look in Xcode's Console, you will see there are some instructions that describe how to solve the problem (Figure 16).
|Figure 16 - Constraint debugging information is displayed in the Console.|
Xcode tells us that it was unable to simultaneously satisfy constraints. It suggests that I probably don't want one of the constraints in the list that it provides. If you look at the list, you can see all of the constraints involve either the star rating control or the image view. Towards the bottom you can see the text "Will attempt to recover by breaking constraint" and it lists the constraint we just created!
So, we obviously have a conflict between two constraints. We want to keep the width constraint, so which other constraint can we delete? I find it easiest to select the user-interface object and look at the other constraints that apply to it.
- If it's not already selected, select the star rating control in the design surface. You will see the constraints shown in Figure 17 where I have highlighted the problem constraint.
|Figure 17 - The horizontal space constraint|
This constraint dictates a specific amount of space between the trailing edge of the star rating control and the leading edge of the image view. That's why the star rating control automatically expands at run time. If we delete this constraint, we should be good to go.
- You can either click directly on the constraint in the design surface, or go to the Document Outline and select the Horizontal Space - (66) Image View - AddImage.png - Mm Star Rating constraint (if you hover your mouse pointer over the node, a tool tip is displayed with the full name of the node.)
- Press the Delete key to remove the constraint from the scene.
- Now let's try it out. Click Xcode's Run button, and when the app appears in the Simulator, select the Write a Review option.
- You should see the star control has maintained its original size as shown in Figure 18!
|Figure 18 - The star control maintains its original size!|
- Go to the Simulator menu, select Hardware > Rotate Left, and you should see the star rating control maintains the same size.
This really just scratches the surface of the things you can do with Auto Layout in Xcode 5. In future posts you will learn more features of Auto Layout as we apply constraints to the other scenes in the app.