SwiftUI foundations

SwiftUI: stacks and groups

Learn how to lay out multiple views in SwiftUI using HStack, VStack, and ZStack to align them on each axis, plus Group to bundle views together.

No real SwiftUI app has just one view. As soon as you want two views on screen, you need a stack.

There are 3 kinds of stacks:

  • HStack lines up views on the X axis, horizontally
  • VStack lines up views on the Y axis, vertically
  • ZStack lines up views on the Z axis, one on top of the other

Let’s go back to the Hello World app:

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello World")
    }
}

To add a second Text view, you might try this:

struct ContentView: View {
    var body: some View {
        Text("Hello World")
        Text("Hello again!")
    }
}

Don’t. You’d be leaving the layout to chance, and older Xcode versions refuse to compile it. You have to embed those views into a stack.

Let’s try with VStack:

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello World")
            Text("Hello again!")
        }
    }
}

iPhone simulator showing VStack with Hello World and Hello again! text views arranged vertically

See? The views are aligned vertically, one after the other.

Swap VStack for HStack and they sit side by side:

iPhone simulator showing HStack with Hello World Hello again! text views arranged horizontally

And here’s ZStack, which puts views one in front of the other. With two texts it makes a mess:

iPhone simulator showing ZStack with overlapping Hello World text views creating messy display

ZStack shines when the overlap is the point. The simplest case: a background image with some text on top of it.

In SwiftUI we organize the whole UI with these 3 stacks, nested inside each other.

Group

There’s a fourth container worth knowing: Group. Like a stack, it bundles several views together. Unlike a stack, it does not affect layout at all.

VStack {
    Group {
        Text("Hello World")
        Text("Hello again!")
    }
}

The two texts still stack vertically, because the VStack decides that. The Group is invisible.

One handy use for Group: a view can have at most 10 children. If you need more, group them, and each Group counts as one child.

Modifiers on containers

Group and the stacks are views too, so they accept modifiers.

Some modifiers only affect the view they’re applied to, like here:

Text("Hello World")
.font(.largeTitle)

Others can apply the same setting to every view inside a container at once.

Like this:

VStack {
    Text("Hello World")
    Text("Hello again!")
}
.font(.largeTitle)

iPhone simulator showing VStack with both text views displaying in large title font size

See? We applied font() to the VStack, and both Text views got the .largeTitle font.

Modifiers that work this way are called environment modifiers. They push a value down to every child. Not every modifier does this, but font() does, and it saves a lot of repetition.

Lesson completed