[iOS] How to Solve UIScrollView Constraint Ambiguity

Ting Yi Shih (Peter Shih)
6 min readSep 25, 2018

Haven’t you ever encountered confusing error messages when you setup constraints for a UIScrollView in iOS interface builder, like this:

Scroll View has ambiguous scrollable content width

Or,

Need constraints for scrollable content height

TL;DR

Delete the constraints between Scroll View and Content View. Now add constrants as follows:

  1. Add alignment constraints, i.e. leading/trailing/top/bottom, on Content View to Scroll View.
    NOTE: If you have more than one Content View, take care of the constraints among them, and add alignment constraints on the outer Content View to Scroll View.
  2. Add explicit constraints for the size of Content View, like equal width and height as Scroll View. Do not assume previous alignment constraints would decide its size!
    NOTE: But if you use Stack View as your Content View, since Stack View has an intrinsic size, you can skip constraints for its size.
Example of constraints on a scroll view, with a nested stack view, without ambiguity

You may now wonder: why do the constraints behave in the way other than I thought?

Explanation

Before solving this issue, let’s recall a casual constraint. Typically, a constraint is the rule that defines one of the following:

  1. How a view is aligned relative to the margin of other views or its super view (leading/trailing/top/bottom constraints)
  2. How a view is sized, either constantly or relative to other views or its super view (width/height constraints)

If a view is fully constrained by alignment constraints (leading + trailing + top + bottom), we usually need not to apply size constraints (width/height) because the size of this view is already determined by the alignment ones.

Therefore, simultaneously assign complete alignment and the size to the content view usually produces conflicts, as below:

Alignment constraints conflict with size constraints

In this figure, we set constraints on a 150 x 150 subview where its leading/trailing/top/bottom must align with its 200 x 200 super view. Boom! Conflicts occur.

You don’t say? Oh… before we go down to the case of UIScrollView, it’s important to point out that: such constraints will behave differently!

UIScrollView

UIScrollView is a special container where the actual content can be larger (or smaller) than its visible content view. Therefore, some of the constraints applied to UIScrollView are given different (or maybe straightforward after you understand it) meaning.

Prepare an empty Scroll View

Let’s start with a simple example.

Put a 150 x 150 subview inside a 200 x 200 scroll view. Set constraints on the smaller subview, where its leading/trailing/top/bottom must align with the scroll view.

In contrast to the previous example, you can find that this time, simultaneously applying alignment and size constraints to a scroll view, which seem to conflict with each other, does not cause conflicts anymore. Instead, you have to apply both to prevent error!

You have to assign both aligning and sizing constraints to Content View

To better clarify, let’s differentiate the outer container view (the light purple view) as Scroll View; the inner subview (the red view) as Content View.

Alignment constraints (leading/trailing/top/bottom)

The alignment constraint between Scroll View and Content View defines the “scrollable range of the content”. For example,

  • If scrollView.bottom = contentView.bottom, it means Scroll View is scrollable to the bottom of Content View.
  • If scrollView.bottom = contentView.bottom + 100, the scrollable bottom end of Scroll View will exceed the end of Content View by 100 points.
  • If scrollView.bottom = contentView.bottom — 100, the bottom of Content View will not be reached even the scrollView is scrolled to the bottom end.

That is, the (bottom) anchor on Scroll View indicates the (bottom) edge of the outer frame, i.e., the visible part of Content View; the (bottom) anchor on Content View refers to the edge of the actual content, which will be hidden if not scrolled to.

Unlike normal use cases, alignment constraints between Scroll View and Content View have nothing to do with the actual size of Content View. They affect only “scrollable range of content view” but NOT “actual content size”. The actual size of Content View must be additionally defined.

Size constraints (width/height)

To actually size Content View, we may set the size of Content View to a specific length, like width/height of 500. If the width/height exceeds the width/height of Scroll View, there will be a scrollbar for users to scroll.

However, a more common case will be, we want Content View to have the same width (or height) as Scroll View. In this case, we will have

contentView.width = scrollView.width

The width of Content View refers to the actual full width of content. On the other hand, the width of Scroll View refers to the outer container frame width of Scroll View. Of course, it doesn’t have to be the same width, but can be other forms like a * scrollView.width + b.

And if we have Content View higher (or wider) than Scroll View, a scrollbar appears.

Content View can not only be a single view, but also multiple views, as long as they are appropriately constrained using alignment and size constraints to Scroll View.

Content View can not only be a single view, but also multiple views

Alignment constraints (centered horizontally/vertically)

Like size constraints, if Content View is centered horizontally/vertically to Scroll View, it is centered relative to Scroll View, i.e. the outer frame.

Usually, centering criteria conflicts with size constraints. Choose only one when you are constraining the size of Content View.

If you use Stack View as your Content View, since UIStackView has an intrinsic size, do not apply additional alignment constraints to it; or they will lead to conflicts. By the way, applying size constraints is O.K. because it’s only overriding the intrinsic size of the stack view.

In a nutshell

In contrast to normal cases, where the size is restricted by the alignment constraints, such restriction is relieved in Scroll View by stretching/shrinking the space of Content View, whereas the size of Scroll View (i.e., the outer frame) remains unchanged. The characteristics of constraints between Scroll View and Content View tells us: the alignment and the size are not contradicted anymore, both of which must be defined to eliminate ambiguity.

When you set up constraints between Scroll View and Content View, keep in mind that:

  1. Content View refers to “the actual full content”, while Scroll View refers to “the outer frame”, i.e., the visible part of the content.
  2. Alignment constraints between Scroll View and Content View define “the scrollable range of the content”, which have nothing to do with the actual size of Content View.
  3. Don’t simultaneously assign Content View the constraints to its size and its alignment.

Understand how constraints work for UIScrollView, and you will not get trapped by the constraint error messages popped up in Xcode!

If this is helpful to you, give me a clap to let me know:)

--

--

Ting Yi Shih (Peter Shih)

Love exploring an elegant pattern that forms robust, maintainable, and understandable coding style.