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.

swiftui vstack

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")
}

swiftui vstack alignment

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
}

swiftui vstack spacing


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")
}

swiftui hstack

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.

swiftui hstack alignment spacing


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.

swiftui zstack layering

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.

swiftui zstack alignment


Combining Stacks

Combining stacks allows you to create much more complex and interesting layouts. You can nest HStacks within VStacks, VStacks within HStacks, and any combination with ZStacks.

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()

swiftui combining stacks

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 the HStack 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")
}

swiftui dynamic spacer hstack

Example with VStack:

VStack {
    Text("Top")
    Spacer() // Pushes "Top" and "Bottom" to opposite ends
    Text("Bottom")
}

swiftui dynamic spacer vstack

Spacers are essential for creating flexible layouts that adapt to different screen sizes.

Fixed Spacers

While dynamic Spacers 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 Spacers 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!