Is it possible for UIStackView to scroll?

Up to date for 2020.

100% storyboard OR 100% code.

This example is vertical:


Here's the simplest possible explanation:

  1. Have a blank full-screen scene

  2. Add a scroll view. Control-drag from the scroll view to the base view, add left-right-top-bottom, all zero.

  3. Add a stack view in the scroll view. Control-drag from the stack view to the scroll view, add left-right-top-bottom, all zero.

  4. Put two or three labels inside the stack view.

For clarity, make the background color of the label red. Set the label height to 100.

  1. Now set the width of each UILabel:

    Surprisingly, control-drag from the UILabel to the scroll view, not to the stack view, and select equal widths.

To repeat:

Don't control drag from the UILabel to the UILabel's parent - go to the grandparent. (In other words, go all the way to the scroll view, do not go to the stack view.)

It's that simple. That's the secret.

Secret tip - Apple bug:

It will not work with only one item! Add a few labels to make the demo work.

You're done.

Tip: You must add a height to every new item. Every item in any scrolling stack view must have either an intrinsic size (such as a label) or add an explicit height constraint.


The alternative approach:

To recap the above approach: surprisingly, set the widths of the labels to the width of the scroll view (not the stack view).

Here is an alternate approach...

Drag from the stack view to the scroll view, and add a "width equal" constraint. This seems strange because you already pinned left-right, but that is how you do it. No matter how strange it seems that's the secret.

So you have two options:

  1. Surprisingly, set the width of each item in the stack view to the width of the scrollview grandparent (not the stackview parent).

or

  1. Surprisingly, set a "width equal" of the stackview to the scrollview - even though you do have the left and right edges of the stackview pinned to the scrollview anyway.

To be clear, do ONE of those methods, do NOT do both.


I present you the right solution

For Xcode 11+

Step 1: Add a ScrollView and resize it

enter image description hereenter image description here

Step 2: Add Constraints for a ScrollView

enter image description here

Step 3: Add a StackView into ScrollView, and resize it.

enter image description here

Step 4: Add Constraints for a StackView (Stask View -> Content Layout Guide -> "Leading, Top, Trailing, Bottom")

enter image description here enter image description here

Step 4.1: Correct Constraints -> Constant (... -> Constant = 0)

enter image description here enter image description here

Step 5: Add Constraints for a StackView (Stask View -> Frame Layout Guide -> "Equal Widths")

enter image description here enter image description here

Step 6 Example: Add two UIView(s) with HeightConstraints and RUN enter image description here enter image description here

I hope it will be useful for you like


In case anyone is looking for a solution without code, I created an example to do this completely in the storyboard, using Auto Layout.

You can get it from github.

Basically, to recreate the example (for vertical scrolling):

  1. Create a UIScrollView, and set its constraints.
  2. Add a UIStackView to the UIScrollView
  3. Set the constraints: Leading, Trailing, Top & Bottom should be equal to the ones from UIScrollView
  4. Set up an equal Width constraint between the UIStackView and UIScrollView.
  5. Set Axis = Vertical, Alignment = Fill, Distribution = Equal Spacing, and Spacing = 0 on the UIStackView
  6. Add a number of UIViews to the UIStackView
  7. Run

Exchange Width for Height in step 4, and set Axis = Horizontal in step 5, to get a horizontal UIStackView.


Apple's Auto Layout Guide includes an entire section on Working with Scroll Views. Some relevant snippets:

  1. Pin the content view’s top, bottom, leading, and trailing edges to the scroll view’s corresponding edges. The content view now defines the scroll view’s content area.
  2. (Optional) To disable horizontal scrolling, set the content view’s width equal to the scroll view’s width. The content view now fills the scroll view horizontally.
  3. (Optional) To disable vertical scrolling, set the content view’s height equal to the scroll view’s height. The content view now fills the scroll view horizontally.

Furthermore:

Your layout must fully define the size of the content view (except where defined in steps 5 and 6). … When the content view is taller than the scroll view, the scroll view enables vertical scrolling. When the content view is wider than the scroll view, the scroll view enables horizontal scrolling.

To summarize, the scroll view's content view (in this case, a stack view) must be pinned to its edges and have its width and/or height otherwise constrained. That means that the contents of the stack view must be constrained (directly or indirectly) in the direction(s) in which scrolling is desired, which might mean adding a height constraint to each view inside a vertically scrolling stack view, for example. The following is an example of how to allow for vertical scrolling of a scroll view containing a stack view:

// Pin the edges of the stack view to the edges of the scroll view that contains it
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true

// Set the width of the stack view to the width of the scroll view for vertical scrolling
stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true