It’s a common problem. You have a screen in your iOS app that looks great on an iPhone 6 Plus and then QA opens a bug because the content is off the bottom of the screen on an iPhone 6. Or perhaps someone hands you new copy for the About text, and it’s longer than the space available. Whatever the reason, you have a screen and now you need to make it scrollable. If only there was a way of doing this without having to disrupt existing code. Well, with Auto Layout, there is.
Let’s start with an example layout. Here is the storyboard for an About screen.
In the constrained design, it looks great. It’s all laid out with constraints so there should be no trouble for Auto Layout to adapt the design to the various form factors our app supports. As a test, here is the preview for 5.5” and 4.7” iPhones.
OK. Auto Layout has handled the title on the narrower screen and our constraints neatly pushed the description down, but the buttons are off the bottom of the iPhone 6 screen. After checking with our designer, the decision is to make the content scrollable on the smaller screen.
Before rushing into the solution to our problem screen, it’s worth going through some Auto Layout theory.
Auto Layout relies on every view’s basic geometry (its size and position) being defined through constraints and/or content. Constraints describe limitations on where and how big a view can be, often in relation to other views. Some views also have an intrinsicContentSize, which is the natural size based on their content. For example, a UILabel will define its intrinsicContentSize to fit the text it has to display, a UIButton will size to include its label and icon, plus any content edge insets. Views that contain other views also have an implied size determined by the size and constraints of their subviews.
The UIScrollView (and its derivatives), however, have a more complicated relationship to Auto Layout, and for good reason. Quite simply, the purpose of a scroll view is to display content that is bigger than itself – having it sized to its content would rather defeat that! Instead, Auto Layout uses the layout of content in a scroll view to set its contentSize, while constraints external to the scroll view determine its size and position. Of course, there are many scenarios where you don’t want Auto Layout managing a scroll view’s content because often the content is loaded dynamically during scrolling, but it is this pattern that we will use to solve our problematic screen.
Our first step is not, in fact, to put our content into a UIScrollView. Before we get there, we must move everything into a UIView. Interface Builder (IB) makes this relatively painless by providing an Embed command in the Editor menu. Just select all the views and choose Embed in View.
Actually, “relatively painless” is correct. Unfortunately, Xcode (at least the current Xcode 6.1) isn’t able to carry over the constraints that are defined in the superview of the views being embedded, which in our case is most of our layout. So the next step is to quickly add them again…
…and we’re back. Now that we have a view that will act as the content view for our scroller, we can go ahead and embed it within a scroll view. Again, you can do this using the Editor menu and choosing Embed in Scroll View.
As we want to scroll the whole view for this screen, next we need to set constraints to bind the scroll view edges to the edges of the top-level view. Then we need to do the same for our content view, setting its edges to the scroll view’s edges.
We are almost finished. There is just one problem, as can be seen from the preview; suddenly our content width has just blown right out so the text views are now each a single line! What happened?
As was mentioned earlier, UIScrollView has a complicated relationship with Auto Layout. Although we added constraints to our content view so its edges coincide with the scroll view, the binding is actually to the scroll view’s content area. In other words, the constraints are telling the scroll view its contentSize, not constraining the content.
We need an additional constraint to fix the width but how can we get the actual width of the screen? Here again Auto Layout makes things easy as constraints are not limited to immediate siblings and superviews. We can create a constraint between our content view and the top-level view. Select both views using ⌘Select and then choose “Equal Widths” in the Pin pop-over. Bingo, the preview is back to normal.
That’s it. No code changes, no fiddling with the scroll view attributes; it just works. On an iPhone 6 Plus, our screen behaves exactly as before, but on the smaller screen of the 6, it magically allows scrolling through the content. And if your app supports landscape, then when rotating the iPhone 6 Plus, the content becomes scrollable or static depending on its layout.
We have demonstrated how Auto Layout can be used to make a screen dynamically scrollable based on layout, but this is just one way in which Auto Layout’s powerful features can deal with many everyday layout issues. If you have any questions or want more information on Auto Layout, feel free to give us a call. We’re always happy to chat.