Let’s build YouI StackView

Shahrukh Alam
Geek Culture
Published in
6 min readJul 21, 2021
Credits: The Venetian

When the Odds are stacked against you, use StackView and chill.

- Pineapple

Did you know a game of Poker once lasted nearly eight and a half years!
Now that I have the most precious gift; your attention, let’s dive straight into what this article is about:

Audience
Beginner, Intermediate
What are we building
Two labels stacked vertically in a UIView which should autoresize itself based on the content width of the bigger label using pure auto layout:

Fig 1

What are we not building
A StackView implementation from scratch, this is more like an understanding of how auto layout works, which is leveraged in a StackView

Iteration 0

Let’s try to do what most of us would do as the first attempt:

Fig 2
  1. Red Label.Leading = Superview.Leading + 20
  2. Red Label.Top = Superview.Top + 20
  3. Red Label.Trailing + 20 = Superview.Trailing
  4. Blue Label.Top = Red Label.Top + 20
  5. Blue Label.Leading = Superview.Leading + 20
  6. Blue Label.Trailing + 20 = Superview.Trailing
  7. Blue Label.Bottom = Superview.Bottom + 20

There is a problem with these set of constraints, both the equations (3) & (6) can’t be satisfied at the same time, which is evident looking at Fig 2

But, what’s interesting is most of the time we get away with it:

  • If you are using Storyboard, it would show error & suggest a fix by bumping either of the Labels horizontal content-hugging and compression-resistance priorities (CHCR priorities).
    Even though it silences the error, it might not be the right solution. Storyboard is pretty smart, but not smart enough to know human needs, in this case we don’t want either of the Labels to grow or shrink.
  • If you are creating views programmatically, things might look okay on the simulator as smaller Label will get stretched & there might not be a background color set to it. But we will get breaking constraints log on the console, which honestly we sometimes conveniently ignore.

Iteration 1

Now that we know constraints are conflicting only on the Trailing side, let’s focus solely on it keeping our requirement in mind.

But, before we do that there is a catch in Fig 2, two set of constraints (for both Width & Height) are not visible to us, which gets added by layout engine automatically for all the views with intrinsic content size. We will focus on the set of constraints for Width as it would help us resolve the conflicting constraints on the Trailing side.

Fig 3: Content Hugging & Compression | Credits: Apple
Fig 4

Layout engine adds following set of constraints for Label’s Width:

For Content Hugging:
Label.width ≤ 0.0 * NotAnAttribute + IntrinsicWidth (Priority: 250)

For Compression Resistance:
Label.width ≥ 0.0 * NotAnAttribute + IntrinsicWidth (Priority: 750)

If you are wondering why priority is more for Compression Resistance, that’s because iOS doesn’t want our content to be clipped, if it’s getting expanded it’s okay for the user.

Now that we know about the hidden constraint by layout engine, we can go back to fixing conflicting constraints only on the Trailing side. Let’s take a step back & think it through what we want or what our requirement is:

  • If we would take only the smaller Label into account, we need a trailing spacing to be greater than 20
  • If we would take only the bigger Label into account, we need a trailing spacing to be equal to 20

If we could club both of these conditions, we will end up with:

  • Both labels will need a trailing spacing to be greater than or equal to 20
  • Both labels might need a trailing spacing to be equal to 20, if it can satisfy the layout engine’s set of constraints for Width. Basically, we can set a lower priority (say 249) to it than the lowest priority (250) of the set of constraints for Width from Fig 4.

Note:
When calculating solutions for the constraint equations:

  • Auto Layout attempts to satisfy all the constraints in priority order from highest to lowest.
  • If it cannot satisfy an optional constraint (a constraint which is not required; with a non-1000 priority), that constraint is skipped and it continues on to the next constraint.

So, Auto Layout will also take into account that both labels might need a trailing spacing to be equal to 20, but it would give more preference to layout engine’s set of constraints for Width. It would keep it if it satisfies other constraints, in our case only bigger label will satisfy.

This is how trailing constraints in focus will look like:

Fig 5
  1. Red Label.Trailing + 20 = Superview.Trailing (Priority: 249)
  2. Superview.Trailing ≥ Red Label.Trailing + 20
  3. Blue Label.Trailing + 20 = Superview.Trailing (Priority: 249)
  4. Superview.Trailing ≥ Blue Label.Trailing + 20

For case 1 in Fig 1, (1) from above can’t be satisfied, but (3) can be. Conversely, for case 2, (3) can’t be satisfied, but (1) can be.

So, the complete working constraints will look something like this:

Fig 6
  1. Red Label.Leading = Superview.Leading + 20
  2. Red Label.Top = Superview.Top + 20
  3. Red Label.Trailing + 20 = Superview.Trailing (Priority: 249)
  4. Superview.Trailing ≥ Red Label.Trailing + 20
  5. Blue Label.Top = Red Label.Top + 20
  6. Blue Label.Leading = Superview.Leading + 20
  7. Blue Label.Trailing + 20 = Superview.Trailing (Priority: 249)
  8. Superview.Trailing ≥ Blue Label.Trailing + 20
  9. Blue Label.Bottom = Superview.Bottom + 20

Time to code

I have made it IBDesignable so that we can see the preview on storyboard by just setting IBInspectable properties. This is how it looks for both the cases from Fig 1:

Fig 7

The above code of VerticalStack will also work for 2 buttons or 1 label & 1 button, basically for any View which has an intrinsic content size.

Note:
It only handles the Width aspect perfectly as I have purposefully not taken Height into account to keep things simple. But, the concept for handling Height remains similar.

Epilogue

In this article with the help of a simple usecase of vertical stack of two labels, we discussed:

  • How constraints are translated to simple mathematical equations
  • How conflicting constraints occur
  • How constraints are automatically generated by layout engine for views with intrinsic content size
  • What are constraint priorities
  • What are content hugging & resistance priorities
  • How auto layout resolves constraints with different priorities

I hope you got an idea on how auto layout thinks & how you should think while laying out views.

That’s it for now, until next time…

Happy Coding & Sharing 😍
Cheers 🍺
Shahrukh Alam

--

--