SwiftUI Stacks: VStack, HStack, and ZStack Combine and Create Complex Screens
It is common to add many elements to the UI when developing an app. Some of them need to be displayed vertically, horizontally or even overlapping. SwiftUI offers us many different methods of accomplishing this, but the most common one is by using Stacks.
In this article, we are going to look at what a Stack is and its three types: VStack, HStack, and ZStack. You will see some simple examples that show the behavior of each of them and the ways to combine them to create complex screens. Finally, we’ll break down a performance comparison between Lazy and not lazy stacks.
What is a SwiftUI Stack?
A Stack in SwiftUI is equivalent to UIStackView in UIKit. It can be defined as a static collection that allows you to include, combine, order, or group a series of user interface elements in specific directions, guaranteeing that the application does not lose its form when it is used on different devices.
An important detail about stacks is that they can only pile 10 subviews, otherwise the message “Extra argument in call” will be displayed on your editor.
The images below present a graphical description of the three available types of Stacks:
VStack:
VStack shows its children elements as a top-to-bottom list. This is the most typically used stack. In fact, this is the default view that the body returns.
In the example below, you will see a VStack with two views: an Image() and a Text():
This is the result:
As you can see, the structure is aligned in a vertical way: the Image() is above the Text().
HStack
HStack is used to group the views left-to-right.
In the example below, you will see an HStack with two views: an Image() and a Text():
This is the result:
As you can see, the structure is aligned in a horizontal way: the Image() is next to the Text().
ZStack
ZStack shows its children back-to-front, allowing the views to be overlapped.
In the example below, you will see a ZStack with two views: an Image() and a Text():
This is the result:
As you can see, the structure is in an overlapping format. So, in this case, the Text() overlaps the Image().
Combining Stacks
Most applications have to show a variety of content that needs to be distributed on the screen. A good way to achieve this is using a combination of Stacks.
In the example below, you will see a combination of VStack, HStack, and ZStack. Each of them have views inside, like Text() and Image().
This is the result:
As you can see, VStack arranges the Text(), HStack, and Zstack as a top-to-bottom list. On the other hand, HStack groups two Text() views, “Agile” and “Unstoppable,” in left-to-right order. Last but not least, the ZStack positions its views, Image() and Text(), one over the other.
Some important details to know about Stacks is that they provide some custom properties, specifically alignment and spacing. The spacing allows the elements of the view to be separated from each other by a specific value (this applies just for HStack and VStack, wherein the alignment arranges the inner views in a specific position).
For example, if we change the definition of VStack to be:
This is the result:
As you can see, the space between the elements has changed, as well as their alignment. Now they are stacked to the left side with a spacing of 40.
Lazy Stacks
We just reviewed the standard Stacks that SwiftUI has, and now we want to know the behavior of these related to the performance. It is worth mentioning that VStack, HStack, and ZStack all load the entire content at once, no matter if the inner views are on-screen or off-screen.
Think for a moment, if we want to present our content inside of a ScrollView, what could be the performance of our application using the standard Stacks? Well, here is where the lazy Stacks come into play.
The term “lazy” comes from the common lazy loading pattern that consists of initializing an object only to the point that it is needed. SwiftUI uses the same concept and applies it to the views so that they can be improved in a number of ways, especially performance.
The lazy Stacks are alternative types of Stacks that SwiftUI provides (available since iOS 14) and come in two different types: LazyVStack and LazyHStack. This means that they don’t apply to ZStack. The main advantage, as mentioned before, is that they can be loaded on demand, making the lazy stacks the typical go-to type of views when using dynamic content that needs to be scrolled.
Let’s clarify this with an example: Say that we need to display not only one Stack with inner views as we did before, but instead want to load a thousand of them. In this case, we turn the VStack into a LazyVStack and put it inside a ScrollView, giving us the advantage of loading the views to the memory when they are brought on-screen:
This is the result:
At this point, there are only four loaded VStacks while the other 996 haven’t loaded yet.
Performance Comparison
In the image below, we compare the performance of memory usage between a LazyVStack and a VStack:
LazyVStack
VStack
For VStack, we just replaced LazyVStack(spacing: 16) {..} from the last code snippet to use a VStack VStack(spacing: 16) {…}. This change caused the memory to go up 48.3 MB because everything was cached from the application boot up.
In Conclusion
Stacks in SwiftUI are ideal for arranging a collection of views in different directions and can be combined to create complex screens. The properties alignment and spacing help us to go one step further and customize our screens depending on our needs.
The lazy Stacks are an improvement from the non-lazy stacks, but this doesn’t mean that they exclude each other. Instead, they can be combined, and it’s important to understand which scenarios we should use each one in.
Resources