UIStackView: Lessons learned

Radim Halfar
INLOOPX
6 min readMar 22, 2018

--

UIStackView is very powerful layout component but it can cause a lot of issues. In this article I will run through some of the common mistakes that we all tend to do while using UIStackView. If you've never experienced any of following, feel lucky!

Observed under Xcode 9.2 & Swift 4.0.

What is UIStackView?

Stack view is a part of the UIKit module. It is a component which automatically manages its subviews. It is derived from UIView whilst also adding great features. You can find the basics and component description on below reference.

The Bright Side 🤩

To summarize the bright side features of UIStackView:

  • layout and arranged subviews positioning
  • can be stacked inside another stack view
  • less constraints (in certain situations)
  • animates while (un)hiding arranged subview
  • wysiwyg (in most cases)
  • supports size classes

Let's come to The Dark Side 👹

Remove arranged subview

It is easy to insert an arranged subview to UIStackView. You can even insert an arranged subview to a specific position. Awesome, how about the view removal?! Many of you might be wondering now, did he read the documentation?! Yes, I did. However why isn't the UIStackView API intuitive in both ways?

Stack View: View insertion

The above snippet instantiates UIStackView and adds arranged subview into it. This is, however, not everything that it does. In fact, the addArrangedSubview function calls addSubview! It means that the view is not only stored as arrangedSubview but also as subview!

Guess what — still there ... ?! 😎

That would be fine if upon calling the removeArrangedSubview the view wouldn't remain in UIStackView subviews. It basically means that you have to call removeFromSuperview subsequently or just write an extension.

Breaking constraints 🤕

Sometimes you may see an unsatisfiable constraints warning. You should only bother with it because it may slow down the view's rendering phase.

Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one that you don’t want.

It's hard to understand which constraint breaks which especially in UIStackView. There is a great online tool called WTFAutolayout. Just paste the contents between the curled brackets and it will summarize the issues for you.

In general, if you are experiencing issues with auto-layout and UIStackView, set the vertical and horizontal constraints to lower priority. It depends what axis of an UIStackView you use. If horizontal, you have to update the leading, and trailing constraints priority of your arranged subviews. If vertical, you have to update the top and bottom constraints priority of your arranged subviews. 999 is enough and will let the UIStackView work smoothly. What UIStackView does behind the scenes, is that it adds 1000 priority constraints to its arranged subviews. Go and check the view debugger and you will see the reason for breaking your valid constraints.

UIStackView+UITableView

Reliable UIStackView+UITableView
You will simplify your layout in IB if you have static content in the table view cell and you do not need to dynamically add or remove any UI elements from the stack view.

Dynamic content of UIStackView in UITableView
If you ever thought it would be a good idea to use UIStackView within your self sizing UITableViewCell with dynamic content, then you should just forget it!

The initial idea was to have it inside the cell in order to easily insert the content into it. Indeed, if it required a sort of animation, it would be pretty easy to animate the insertion or removal. How I was wrong…

Known issues

  1. Laggy while rendering variety of contents with different sizes
    Potential cure: Compute the cell height manually
  2. Needs to be cleaned in prepareForReuse(_:)
  3. Animation is annoying by either requesting layoutIfNeeded() on UIStackView or calling beginUpdates(), endUpdates() on UITableView while (un)hiding the view just before the call
  4. The (un)hide has to be put into animation block to have nice and fluent animation though 😒
  5. Often breaks constraints (refer to the 999 rule 👾)
    Potential cure: For vertical UIStackView you have to set the constraints pointing to superview's top and bottom to lower priority. 999 is fairly enough.

UIStackView+UIScrollView

Another issue you might came across while using UIStackView is when using it with combination of UIScrollView.

UIStackView+UIScrollView setup

Consider the situation as seen on the image. Your layout consists of UIScrollView where UIStackView is embedded. You are using autolayout in your code and stack view is bound by constraints to its superview. The Interface builder is now complaining about the content size of UIScrollView which is pretty inconvenient if you want to insert items to UIStackView during the application runtime. If you change the bottom constraint to greater than or equal it won't help. You have two options depending on the use case.

Adding arranged subviews in Interface Builder
In this setup, you can easily add arranged subviews in interface builder. However, in this setup if you add any arranged subview to your stack view it fills the whole screen which is not desired. Now it's time to remove constraint center vertically in container of UIStackView. It works like a charm if you have at least one view added to stack view in IB.

Adding arranged subviews in code 🤞
In case you need to use dynamic layout setup these are the issues you might face.

  1. UIScrollView requires constraint for x / y position
  2. Add arranged subview with height set to 0
  3. Ignore constraints and add it during runtime
  4. You can(‘t) ignore the warnings in interface builder

The proper way to set the UIScrollView including UIStackView is to use option #1.

#1 UIScrollView requires constraint for x / y position
Set the width of the UIStackView equal to the UIViewController view. Also add the height constraint to UIStackView marked as removed at build time.

Proper layout setup

#2 Add arranged subview with height set to 0
To satisfy the request for valid content size of the UIScrollView you can add a placeholder view, which height should be set to 0. This will resolve the errors in the Interface builder.

Known issues summary:

  • Invalid content size of UIScrollView since it should be empty and has size of {0, 0} including arranged subview
  • If setting spacing, the 0 height arranged subview causes problems

Potential cure:

  • Remove the placeholder view in runtime prior adding any views. (This requires boilerplate code which can be omitted with solution #1❗)

#3 Ignore constraints
You might decide to ignore the constraints or you just decide not to work with IB at all. In this case you have to create your constraints in code which brings a lot of boilerplate code which might be done in IB. However if you are used to doing so, keep your solution consistent and continue. Prior to the view setup, you add any arranged subview to UIStackView and then you need to setup the constraints and pin the UIStackView to UIScrollView.

#4 Ignore the warnings in interface builder
You might decide to ignore the warnings and errors in IB setup. This isn't a good idea at all since it might hide other UIStackView unrelated warnings and your layout might break up entirely.

I don't suggest this approach unless it is possible!

Known issues summary:

  • Hides unrelated IB issues
  • Might break the auto-layout setup and lead to undefined behavior
  • Leads to hard to find issues
  • Produces errors while using lint's such as IBLinter

There is no potential cure, just don't do it!

Make UIStackView you friend, not an enemy

  • Do not turn off auto-layout unless it is inevitable and deal-breaking
  • Do not try to hack the component. It is designed to be used as it is designed!
  • Do not put UIScrollView as arranged subview. You will end up with an infinite scroll view.
  • Do not break constraints! Use 999 rule.
  • Do not use it in dynamically sized UITableView while having dynamic content in an UIStackView. It will bring you more issues than benefits.
  • If being reused, clean it up from unwanted subviews.
  • It is an UI component, access it from main queue.

If you've ever experienced any issue with UIStackView, believe it or not, you are not alone.

Examples can be found on my github page.

--

--