UIStackView in Swift Part 1
In June 2015, something finally changed in terms of layout in iOS. On WWDC Apple introduced a new layout tool for developers – UIStackView. Well known to OSX developers, this great layout view finally came into iOS world. Let’s be honest – AutoLayout is not trivial – for experienced developers is logical and enable them to create beautiful and well-organized designs that looks terrific on any device. Unfortunately, there are many caveats for beginners and even pre-intermediate developers that make AutoLayout the source of many headaches. Not always obvious constraints to apply or creating too many of them, handling rotation and different screen sizes(this has been dramatically changed in iOS 8 with introduction of size classes) and adding new, dynamically presented views to already constrained view.
Let’s say you want to dynamically present some label in a table view cell. How to create constraints to this view? You could add constraints in relation to this label, but you have to delete the previous ones. Moreover, this label will be presented in rare occasions, so we are going to hide it. Constraints will be confused – where the label did go? How should we layout visible views? So many questions and so little answers. Fortunately, UIStackView comes to help!
We are going to create app that shows us the power of UIStackView – at the beginning, some basic examples to grasp the idea of this layout view, and after that we are going to create some great looking views that you can use in your app. All these features we are getting for free. Without UIStackView we would need many constraints and boilerplate code to handle animations and hiding views.
First steps
You’ll see already written code for logic, so you can focus on layout of the app. Let’s see what’s going on in storyboard:
(I’ve already connected IBOutlets to views for you, don’t worry.)
Our goal is to present collections of funny animals in good old UITableView. On the left we have a thumbnail image for collection, optional label indicating new collection when we tap + button in the upper-right corner. Next is the title for collection and another optional label with description. In app we’ll show it with neat animation by tapping info button.
Unfortunately, when we run app, we’ll see really messy layout:
No wonder – we didn’t add any constraints. Here comes UIStackView for the rescue!
Adding UIStackView
How to use it? The first step is to stack views. Select all subviews of the cell and click Stack button(that with bars and arrow), under your controller.
Okay, looks better! But now we have a problem – Xcode complains about missing constraints – a lot of them. And we wanted to use as little as possible, right? Don’t worry – we’ll use… 4. In total, for UIStackView only.
Select Stack View in navigator and click Pin button under storyboard(looks like duck or TIE-Fighter):
Click 4 dotted bars and enter reasonable values for margin for our stack view, like 2. Don’t worry if you previously had some ridiculous values. Click ‘Add 4 Constraints’.
Fixing priorities
Now we should see all subviews. But we have new errors… Be advised – you might not see them. On Xcode 7 beta 2 you won’t see it, but on Xcode 7.1 beta 1 you will(at the top 7.0, at the bottom 7.1):
Definitely Xcode 7.1 is more helpful here. All subviews have the same value for content hugging priority: 250. They want to grow and thumbnail have enough space. It should be big enough to present our squared picture. Change it to high priority, or 750:
Thumbnail is now positioned correctly, but the rest – not really. For New Label and Description Label you can set something a little bit bigger than 250, like 255. But for title label choose, for instance, 252, and for Info button – 253. Now errors should be gone and layout should look just like we wanted – thumbnail on the left, info button on the right, and labels attached to the thumbnail.
Layout still needs some improvements. Subviews are too tightly aligned, so select stack view and change Spacing to 10:
Moreover, we want to have description of our collection underneath the title label. How to do this? Stack view has set horizontal axis, but for labels we want to set vertical.
The cool thing about stack view is you can create nested stack views. Just select title and description label and click stack button, just like before.
You will probably see something like this:
First of all – change the axis of nested stack view to vertical. If title label is underneath description, drag it to the top of stack view. To eliminate yellow warnings, decrease content hugging priority; we won’t need it as we stacked these subviews. The last thing is to align label to the left, so set Distribution to leading. Now layout should look clean:
We’ve set only 4 constraints and our cell looks great on any device! How cool is that?
What’s more, UIStackView is really flexible container view. To demonstrate this, we want to add 2 features:
- When we tap plus button, a new collection is inserted into table view with label “New!”. When we tap cell – label hides.
- Description is hidden, but when we tap info button it’s going to show with lovely animation.
And of course, all of that without additional constraints, calculations, etc.
New features
First feature is really simple, just few lines of code:
1 2 3 4 5 6 7 | override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { animalsCollection[indexPath.row].isNew = false let cell = tableView.cellForRowAtIndexPath(indexPath) as! AnimalsCollectionCell UIView.animateWithDuration(0.5) { cell.isNewLabel.hidden = true } } |
That’s it! Label now hides as we tap new collection. What’s more, title and description go to the previous position without playing with constraints.
Let’s move to second feature. Here’s a caveat – we want to have a reference of table view cell that contains tapped info button. Fortunately you don’t have to do that – I’ve included handy extension for UITableView that returns a reference to cell which contains tapped button.
Here’s the implementation of method for tapped info button:
1 2 3 4 5 6 7 8 9 10 11 12 | @IBAction func showMoreTapped(sender: UIButton) { if let cell = tableView.cellWithSubview(sender) { UIView.animateWithDuration(0.3, animations: { cell.descriptionLabel.alpha = cell.descriptionLabel.alpha == 0.0 ? 1.0 : 0.0 }) { finished in UIView.animateWithDuration(0.5) { cell.descriptionLabel.hidden = !cell.descriptionLabel.hidden } } } } |
The method goes as: if I found cell that contains tapped button, change description alpha to 1.0 if it’s hidden, otherwise to 0.0. When this finished – if description is hidden – show it. Otherwise – hide it. Simple as that. Why bother changing alpha and just change hidden value? Well, you can just manipulate hidden value, but changing alpha too looks little bit better(at least for me).
Finally, go back to storyboard, select description label and check Hidden option. Now description will only show when we tap info button.
Here’s the final result:
Conclusion
As you can see, creating cell with UIStackView is really simple and can save you some time, instead of wasting it for constraints modifications. Moreover, you can easily add more buttons and labels to cell, without worying to add and change constraints.
In the next part, we are going to look at some more advanced ways to leverage the power of UIStackView. Stay tuned!
Ready to take your business to the next level with a digital product?
We'll be with you every step of the way, from idea to launch and beyond!