We’ve all been there: that moment when you start laying down the UI of your app window and it all looks great.
But then you have to make it practical.
Once there are more than a few Cocoa controls laying around, you start planning how to put Auto Layout to work so that all your views reposition and resize as desired when the user resizes the app window.
The fun starts when you add constraints in Interface Builder — things can get complex very quickly. Often, you’ll end up with constraints that contradict each other and you need to retrace your steps to find the offending constraint and adjust it to play nicely with the rest.
Stack views were introduced with the release of OS X Mavericks, and ever since, they’ve spread to watchOS (well, a similar form at least) and iOS. The APIs for each platform differ to reflect the needs of each UI paradigm, but the core concept of leveraging the power of Auto Layout without the need to use constraints remains the same.
Note: This NSStackView
tutorial assumes basic familiarity with Auto Layout. If you’re new to Auto Layout go check out the Auto Layout introduction tutorial, because iOS concepts are very similar to those used for OS X.
What is a Stack View?
A stack view allows you to arrange a number of views in a stack; it looks much like a table with a single column or a single row, depending whether you set a horizontal or vertical orientation for your stack:
At first glance, it might not look like much, but you’ll be surprised how much power you gain from a simple stack. You’ll also enjoy greater control of spacing between the arranged views, their alignment, and so on.
And finally, you can nest stacks. That’s where the real fun starts.
When to Use a Stack View in Your OS X App?
A stack view is not a silver bullet to all of your UI problems, but it does make many day-to-day tasks much easier. For instance, sometimes you can design the complete layout of a window without creating a single constraint, and that’s a pretty big win!
Stack views are pretty handy in a number of cases, for instance when:
- You plan on using split view but don’t need the user to be able to resize its subviews
- You have a view on top or the sides of the window that shows and hides often, e.g. a notification bar
- You need to align groups of controls in any table-like layout
- You need a custom toolbar somewhere inside a view
- And so many more…
To say there are lot of applications for a stack view would be an understatement. Once you finish this tutorial and try some stack view magic you’ll be able to spot opportunities where they can help your layout within your apps.
The Tutorial Project
In this tutorial, you’re going to work on an OS X app and implement a complex UI based on stack views.
One of the key points you’ll learn is how to customize a stack view layout beyond the built-in properties. Finally, you’ll build UI animations based on stacks.
By the time you’re finished, the app will be a fully functional raywenderlich.com book store and it will look like this:
Getting Started
In this NSStackView
tutorial, you’ll work on an app called Book Shop. It’s a complete working app that allows people to browse books on the raywenderlich.com store and purchase them through the actual store that opens in their browser.
Start by downloading the starter project for this tutorial: BookShop-starter.
Open BookShop.xcodeproj and select Main.storyboard to have a look at the current state of the app’s interface. You’ll see that someone had a hard time designing the UI and pretty much left you a big mess:
No fear — thanks to stack views, finishing the app layout is as easy as can be!
Your First Stack View
Creating stack views in Interface Builder is really easy. In fact, you better pay close attention because you might blink and not notice you created them. :]
For your first steps with stack views, you’re going to focus on the part of the app that shows the text data about the selected book: the title, the current edition and the publisher:
Your first task is to align the labels Title and iOS Animations by Tutorials in a horizontal stack. This will keep those two nicely aligned.
Select the Title label, then while pressing the Command key on your keyboard, select iOS Animations by Tutorials.
Now find the stack button at the bottom of the Interface Builder panel and click it once:
Once you click the Stack button, look back at the labels: they now look like one entity, and that’s your first stack view!
But what happened? You had two views selected.
When you clicked on the stack button, Interface Builder checked the relative position between the selected views and assumed you wanted to create a horizontal stack! Check the Attributes Inspector that shows the stack properties:
But what if Interface Builder guessed wrong? How difficult is to have a vertical stack instead?
It’s as simple as choosing Vertical from the orientation drop-down and checking the result:
That was easy! Now go back to Horizontal for orientation and let’s move on!
Since you’re almost a pro by now, stack up the rest of the labels thusly:
- Select Edition and 1st edition and click the stack button to stack them
- Select Publisher and Razeware LLC and click the stack button to stack those too
Your layout should now look like this:
You now have all the labels aligned horizontally in pairs. Notice how you have the same spacing between sets. Each stack view applies the default spacing of 8 points between its views.
Good going so far! Things are looking organized. :]
Nesting Stack Views
You’ve seen how easy it is to organize labels in stacks; it cost you a few little clicks. But wouldn’t it be great if you could somehow organize the three rows of text you ended up with too?
Good news — this task is almost as easy! First of all, find the document outline button towards the lower-left corner of Interface Builder, and in case you don’t already have document outline pane open, click the button to do so.
In your document outline, select the three stack views while holding the Command key on your keyboard, like so:
I hope you already guessed the next step. Click the Stack button in the bottom right to stack together those…stack views!
Now you have three horizontal stack views stacked vertically! It looks like a little table, and that’s precisely what you wanted.
Now it’s time to look into another property of stacks that you definitely need to use when nesting stacks. The alignment property (look it up in the Property Inspector while the stack view is selected) allows you to set how the views should be aligned in the alternate axis of the stack’s orientation:
- For Horizontal stacks, Alignment lets you arrange views on their top, center, bottom or base line
- For Vertical stacks, Alignment lets you arrange views on their leading, center and trailing
Feel free to play with the current stack’s orientation, but ultimately, set it to Center X to center all the text rows, like so:
Hey that looks pretty cool! And to keep momentum up, go ahead and take care of that cover image too. In the document outline, select both the stack view (the one you just created) and the book cover image:
I guess by now there’s almost no need to say it but in the spirit of being totally clear I will: click Stack to stack those two together! Set the orientation of the new stack to Vertical to arrange the image and the text above each other.
In your app, however, you want the image to appear above the texts, unlike the current arrangement. So, unfold the stack view in the document outline and drag the image view above to re-order the arranged views, like so:
The image view should remain a sub-view of its original parent and should just appear above the Stack View, which is its sibling. As soon as you do that the views will appear like this in Interface Builder:
Now change alignment to Center X and spacing to 12. This will center the image and text horizontally and add a bit of spacing between them.
Finally, select the last stack view you created and the Buy now from raywenderlich.com button. Again, click the Stack button and make sure orientation is set to Vertical, alignment is Center X and spacing to 50.
Your final layout should look like this:
Customizing the Stack
So far, you’ve created a number of stack views and hopefully you’re starting to feel like a pro. :] You have the default stack view configuration, however, and in this section you’ll see how customizing the default behavior can provide even more flexibility at almost no cost.
The stack views you created so far tutorial grew in size along with their content. In a way, you’ve only been “wrapping” views together for the sake of aligning them and nesting stacks.
Stacks though can behave a bit differently if you fix their size and let them arrange their sub-views within that given space.
Full Window Layout
So far, you’ve got two “top” layout elements in your app’s UI. The former is the table on the left, and the latter is the stack view that wraps all the details about a single book plus the purchase button.
No matter how you arrange those elements you ultimately want them to spread nicely within the app’s window.
In the document outline select the table view and the top stack view you have so far:
Next — no surprise here — click the Stack button at the bottom of the Interface Builder pane to stack the two selected views together, effectively bundling them into a new horizontal stack.
Now click on the Pin button, which is located close to the Stack button, and enter four zeroes in the four boxes at the top of the popup. Make sure the four red lines light up while you enter the numbers in.
Finally, click Add 4 Constraints to pin your top stack view to the window, effectively making it a “full window” view.
Just to make sure all views display correctly, click Resolve Auto Layout, located to the right of the Pin button, and choose Update Frames under All Views in View Controller. This will apply all current constraints and your layout will look like this:
Since the table view has a constraint that sets its width to 180
points, the stack view respects that and lets the other arranged view fill all of the remaining space. Run the project and try resizing the window.
The table view fills up the window vertically but it always keeps its width because the width is pinned with a constraint.
Shift your attention to the book details. As you resize the window, the cover image and the texts always stay centered. Since the stack that contains them grows or shrinks to fill up the space not taken by the table view, its sub-views always stayed centered.
This is all you needed to do to make your layout fill up the window. That was easy, n’est–ce pas?
Now look into another area of the layout where some of that same magic you just did could come handy.
Full Table Cell Layout
The table view doesn’t look all that nice at the moment, kind of like someone just threw in an image view and a label without any concern for alignment. Annoyingly, the text gets cut out at run time:
Time for you to add some stack view goodness to that table view.
In document outline, select the cell image and label. To do that, you’ll need to drill down through the view hierarchy as shown:
Aaaand…drumroll…you guessed it: click the Stack button in Interface Builder. This will bundle the image and label into a vertical stack view.
With the stack still selected, set the alignment to Center X. While you still have the stack selected, click on the Pin button at the bottom of Interface Builder and pin the stack to its parent view in all directions.
Since the nearest neighbor view of the stack view is the cell view, simply pin the stack to the cell itself. Click Add 4 Constraints to finish up and close the popup.
Select the stack view and click the Resolve Auto Layout issues button (to the right of Pin) and select Update Frames. The stack view takes up the whole table cell space and it now looks like this:
Run the app and note how the stack view makes your whole table layout work like a charm.
Using Constraints on Arranged Views
Stack views give you the power of Auto Layout without the hassle of creating all the constraints yourself, but you can still design constraints manually if you want. And sometimes, you do want to do that; sometimes default settings just don’t work. :]
There’s a few small issues with your layout that would benefit from a few constraints.
Select the Buy now from raywenderlich.com button and click the Pin button at the bottom of Interface Builder. From the popup menu, click the checkbox next to width and enter 250 in the box.
Click Add 1 Constraint to make it so. You’re setting the button width to 250 points, giving it nice padding on the sides so it’s easy on the eyes.
Adding custom constraints is that easy!
How about you align the window contents to the top of the window? Start by selecting your current top stack view.
In Attributes Inspector, set alignment to Top. This should move the book details views up the window, like so:
Now you’re going to add a bit of spacing on the top so that the cover image is not “stuck” to the window title bar. Select the stack containing the book details (second down the hierarchy):
Click the Pin button and add a Top constraint with the value of 20 points:
Click Add 1 Constraint, and you’ll see that the book details stack view now has 20 points as a top margin.
As you see, you can leave stacks to apply the default behavior and just add a constraint here and there to customize and perfect the layout!
Working with the Arranged Views
Now that you know how to stack up your layouts, the next level is to play around with the stack contents.
The stack view itself is just a container and displays nothing on screen. It merely arranges the layout of a bunch of views, and you can access those via the arrangedSubviews
property. For each arranged view, you can show and hide, animate, or remove it from the stack at will.
Stack View Animations
In this last section you’re going to learn the basics of working with arranged subviews.
First of all, in order to access your stack view from code, you need to create an outlet for it. Open ViewController.swift and add a new outlet variable to the class:
@IBOutlet weak var topStack: NSStackView! |
Now switch back to Main.storyboard and drag from your ViewController to the top stack view in the document outline while pressing Command on your keyboard:
From the popup menu, select topStack, and voila! Your stack outlet is connected.
Note: Now that you have a live outlet to the stack view you can work with it from code in the same manner you do for labels, button, and other Cocoa controls. For example you can dynamically add arranged view by calling the addArrangedSubview(_:)
method on your stack view or remove and arranged view by making use of removeArrangedSubview(_:)
.
Run the app and check the button in the top-right corner of the window:
The menu button is selected by default, but if you click it repeatedly you’ll see it toggles between selected and deselected states. The button is already connected to the method in ViewController
called actionToggleListView(_:)
, so you can just add your code at the bottom of the method body.
First, add an empty animation at the bottom of the method:
NSAnimationContext.runAnimationGroup({context in //configure the animation context context.duration = 0.25 context.allowsImplicitAnimation = true //perform the animation }, completionHandler: nil) |
runAnimationGroup()
allows you create UI animations by simply listing the desired changes in the argument closure. The animations closure gets one parameter, which is the animation context – you can adjust various aspects of the animation by changing the properties on the context. So far you set the animations duration and you enable implicit animations.
Note: Unlike on iOS, in OSX there are different (but similar in effect) APIs to create animations. I personally like using NSAnimationContext.runAnimationGroup(_)
because it’s the closest to what I’m using on iOS and can write my code easier and faster by just using the same approach on both platforms.
Next you can just toggle the visibility of the first arranged view of the top stack — more specifically, the table view that shows the list of books. To make all changes in the window layout animate nicely, also add a call to layoutSubtreeIfNeeded()
.
Just below the comment //perform the animation
(but still inside the closure) insert this:
self.topStack.arrangedSubviews.first!.hidden = button.tag==0 ? true : false self.view.layoutSubtreeIfNeeded() |
This will hide or show the book list each time you click the button. Since you made your changes from within an animation block, the layout flows nicely to accommodate your changes.
Note: Just like with any other animation under Auto Layout you need to force a layout pass from inside the animations block to animate the changes you do to your views. In fact changing the hidden
property on your first arranged view will be automatically animated, the rest of the arranged views however will have to change position and you’d like to animated those changes. That’s why at the end of the block you make a call to layoutSubtreeIfNeeded()
.
And there you have the completed raywenderlich.com book store project!
For kicks, choose your favorite book from the list and click on the purchase button. This will open your default web browser and take you directly to the book store page:
Where to Go From Here
To see the completed project, download BookShop-completed.zip.
This tutorial covered quite a bit! You know how to:
- Align views in your UI by bundling them in stacks
- Design complex layouts with nested stack views
- Use constraints to customize stack layouts
- And finally, how to interact with the arranged subviews
If you’d like to learn more about stack views and how to use them for fun and profit, consider the following resources:
- Apple’s NSStackView docs: Here you’ll discover more about this class’s properties and methods.
- Mysteries of Auto Layout, part 1: In this WWDC ’15 talk you’ll learn about the motivation behind stack views and see some live demos.
- Finally, watch the UIStackView video series right here because most of the concepts on iOS and OS X are the same.
- if you’d like to learn more about how the book list was coded in the starter project check out this great tutorial about Cocoa table views
If you have any questions or comments on this tutorial, feel free to join the discussion below in the forums! Thanks for joining me again!
The post OS X Stack Views with NSStackView appeared first on Ray Wenderlich.