SwiftUI provides three basic containers for arranging views: HStack
, VStack
, and ZStack
. These stacks are used for creating simple and complex user interfaces. Let’s see how they work.
VStack: Arranging Views Vertically
A VStack
(Vertical Stack) arranges its child views in a vertical line, one on top of the other.
Example:
VStack {
Text("Top Item")
Text("Middle Item")
Text("Bottom Item")
}
This will display three text views stacked vertically.
By default, views within a VStack
are centered horizontally. You can control the alignment using the alignment
parameter:
VStack(alignment: .leading) {
Text("Aligned to Leading Edge")
Text("This too")
}
You can also add spacing between items:
VStack(spacing: 20) {
Text("Item 1")
Text("Item 2") // There will be 20 points of space above this text
}
HStack: Arranging Views Horizontally
An HStack
(Horizontal Stack) arranges its child views in a horizontal line, side by side.
Example:
HStack {
Text("Left Item")
Text("Center Item")
Text("Right Item")
}
This will display three text views arranged horizontally.
Similar to VStack
, HStack
also has an alignment
parameter (for vertical alignment within the HStack
) and a spacing
parameter.
HStack(alignment: .top, spacing: 15) {
Image(systemName: "star.fill")
Text("Favorite")
}
In this example, the star image and the “Favorite” text will be aligned to their top edges with 15 points of space between them.
ZStack: Layering Views
A ZStack
(Depth Stack) overlays its child views, arranging them on top of each other along the z-axis (depth). The first child view in the ZStack
is at the bottom, and subsequent views are layered on top.
Example:
ZStack {
Color.blue // Bottom layer
Text("Text on top of color") // Top layer
}
This will display blue background color with the text “Text on top of color” rendered over it.
Common Use Cases:
- Placing text or controls over an image or background color.
- Creating custom UI elements that require overlapping views (e.g., badges on icons).
- Implementing overlay views like loading indicators or pop-up messages.
ZStack
also has an alignment
parameter, which controls how the child views are aligned within the stack’s bounds (the rectangular area the ZStack
occupies on screen). For example, alignment: .topLeading
would align all children to the top-left corner.
ZStack(alignment: .bottomTrailing) {
Image(systemName: "figure.fishing.circle")
.resizable()
.aspectRatio(contentMode: .fit)
Text("Photo Credit: SF Symbols")
.padding(4)
.background(Color.black.opacity(0.5))
.foregroundColor(.white)
.font(.caption)
}
In this example, the “Photo Credit” text will be placed at the bottom-right corner of the ZStack
, overlaying the background image.
Combining Stacks
Combining stacks allows you to create much more complex and interesting layouts. You can nest HStack
s within VStack
s, VStack
s within HStack
s, and any combination with ZStack
s.
Example: A Simple Profile View
VStack(alignment: .leading, spacing: 10) {
HStack(spacing: 15) {
Image(systemName: "star.fill")
.resizable()
.frame(width: 60, height: 60)
.foregroundColor(.blue) // Light saber blue!
VStack(alignment: .leading) {
Text("Luke Skywalker")
.font(.title)
Text("Joined: A long time ago")
.font(.subheadline)
.foregroundColor(.gray)
}
}
Text("Bio: Jedi Master, prefers X-Wings over TIE Fighters.")
.font(.body)
}
.padding()
This example uses:
- A main
VStack
to arrange elements vertically. - An
HStack
inside it for the profile picture (perhaps a Rebel insignia or a generic Star Wars figure) and user details. - Another
VStack
inside theHStack
for the name and join date.
Spacers
SwiftUI provides a Spacer
view, which is super useful within stacks. A Spacer
dynamically expands to fill available space along the stack’s axis.
Example with HStack
:
HStack {
Text("Left")
Spacer() // Pushes "Left" and "Right" to opposite ends
Text("Right")
}
Example with VStack
:
VStack {
Text("Top")
Spacer() // Pushes "Top" and "Bottom" to opposite ends
Text("Bottom")
}
Spacer
s are essential for creating flexible layouts that adapt to different screen sizes.
Fixed Spacers
While dynamic Spacer
s are great for flexible layouts, sometimes you need a fixed amount of space between elements. You can achieve this by providing a minLength
to the Spacer
initialiser. This tells the spacer to occupy at least that amount of space along the axis of its parent stack.
Example with HStack
:
HStack {
Text("Item A")
Spacer(minLength: 50) // Creates a fixed 50-point horizontal space
Text("Item B")
}
Example with VStack
:
VStack {
Text("Item X")
Spacer(minLength: 30) // Creates a fixed 30-point vertical space
Text("Item Y")
}
In these examples, Spacer(minLength: 50)
in an HStack
or Spacer(minLength: 30)
in a VStack
creates an invisible view that occupies at least that fixed amount of space, pushing other elements apart by that specific dimension. If the available space in the stack is larger than minLength
, the spacer will still expand to fill the remaining space, just like a default Spacer
. To ensure it only takes up the minLength
, you might combine it with .fixedSize()
on the Spacer or ensure the stack itself isn’t trying to expand it further, but for simply guaranteeing a minimum separation, minLength
is the direct approach.
Conclusion
HStack
, VStack
, and ZStack
are the foundational tools for layout in SwiftUI. By understanding their individual behaviors and how to combine them, you can construct a wide variety of user interfaces. Remember to also leverage Spacer
s and the alignment
and spacing
parameters to fine-tune your designs.
As you build more complex views, you’ll find yourself reaching for these stacks constantly. They are simple, yet powerful.
Happy stacking!