Scroll View Layouts With Interface Builder

Laying out a scroll view is a confusing task. I found it easier when, in iOS 11, Apple introduced frame and content layout guides. Too bad they neglected to add them to Interface Builder. That changed in Xcode 11. Here’s a quick guide on how to use them.

Why Are Scroll View Layouts So Hard?

For a recap on why scroll views are confusing and how the frame and content layout guides help see this earlier post on easier scrolling with layout guides. Here’s the layout I built in code using layout guides:

Scrolling layout example

You need two groups of constraints to layout a scroll view:

  • constraints that fix the frame (size and position) of the scroll view relative to its superview.
  • constraints that layout the content relative to the content area of the scroll view and constrain its size.

Building my layout with Interface Builder and Xcode 10 I might end up with constraints like this:

Constraining a scroll view (Xcode 10)

We have a total of nine constraints that I think about in three groups:

  1. Four constraints pin the scroll view to the edges of the root view, fixing its frame.
  2. Four constraints between the stack view and the scroll view pin the content to the scroll view content area.
  3. The width of the stack view is fixed to the width of the scroll view frame.

I never found this to be very intuitive:

  • Constraints between the scroll view and a super view act on the frame of the scroll view.
  • Constraints between the scroll view and a subview act on the content area of the scroll view.
  • Height or width constraints between the scroll view and a subview use the frame of the scroll view.

The introduction of the frame and content layout guides in iOS 11 promised to make things clearer. Unfortunately, Apple neglected to add support for them in Interface Builder.

What Changed In Xcode 11?

From the Xcode 11 release notes:

Content and Frame Layout guides are supported for UIScrollView and can be enabled in the Size inspector for more control over your scrollable content. (29711618)

The content and frame layout guides are enabled by default in Xcode 11 when you drag a new scroll view into the canvas:

Scroll View Content Layout Guides

You can enable them for an older layout using the size inspector:

Enable Content Layout Guides

The best way to explain how to use them is with an example.

Using Content Layout Guides In Interface Builder

Let’s build the previous layout in Interface Builder using the content layout guides:

  1. To get started I have arranged the content for my layout in a vertical stack view that I have embedded in a scroll view in Interface Builder:

    Embedded stack view

  2. Let’s fix the size and position of the scroll view by pinning it to the edges of the root view. I find that easier to do if you first hide the safe area layout guide for the root view (using the size inspector):

    Hide safe area layout guide

    Select the scroll view and then use the “Add New Constraints” tool to add the four constraints between the scroll view and the edges of the root view:

    Pin scroll view

    Check the constraints in the navigator to make sure you’re not using the safe area or accidentally adding padding to any of the constraints:

    Scroll view frame constraints

  3. We want to pin the stack view to the edges of the content area. Control-drag from the stack view to the Content Layout Guide:

    Using the content layout guide

    Then add the four constraints to pin each edge of the stack view to the guide:

    Constraints with the content layout guide

    Check the constraints in the navigator:

    Content constraints

  4. I don’t want the layout to scroll horizontally so I also need to fix the width of the content area to match the width of the scroll view frame. It’s not possible to create an equal width constraint between the content and frame layout guides in Interface Builder. Instead, control-drag from the stack view to the frame layout guide:

    Using the frame layout guide

    Then add an equal widths constraint:

    Equal width constraint

    The final set of nine constraints using both the content and frame layout guides:

    Completed constraints

What’s Still Missing?

I don’t want to complain. It’s good to see the content layout guides show up in Interface Builder. It’s been two years since they were introduced with iOS 11 but better late than never. To complete the picture I’d like to see Apple also expose the layout margin guide for the scroll view.

Sometimes you want a view that remains fixed in the scroll view frame so that it floats over the scrollable content. For example, the info button in my previous example remains fixed in the top-left corner when the content scrolls:

Info button

The frame layout guide doesn’t help in this case as it extends into the safe area behind the navigation bar. I created constraints in code to fix the button to the layout margins guide of the scroll view:

infoButton.leadingAnchor.constraint(equalTo:
           scrollView.layoutMarginsGuide.leadingAnchor),
infoButton.topAnchor.constraint(equalTo:
           scrollView.layoutMarginsGuide.topAnchor)

If you try to do that in Interface Builder the constraints are assumed to be with the content area so the button scrolls with the content. The closest you can get is to use the safe area layout guide of the scroll view (enable it in the size inspector) with some extra padding:

Scroll view safe area layout guide

See The Code

You can find the code for this post in my Code Examples GitHub repository. You can compare it to the original ScrollGuide project which builds the layout in code.

Read More