Layout: Stacks, Spacers & Alignment
The three stacks
SwiftUI layout is built around three container views. Almost every screen is some combination of them:
- VStack — arranges children vertically (top to bottom).
- HStack — arranges children horizontally (left to right).
- ZStack — layers children on top of each other (for overlays/backgrounds).
VStack(alignment: .leading, spacing: 8) {
Text("Anand Gaur").font(.headline)
HStack {
Image(systemName: "star.fill").foregroundStyle(.yellow)
Text("Pro member")
}
}
Spacing and alignment
Each stack takes a spacing: (gap between children) and an alignment: (how children line up on the cross axis). For a VStack, alignment is horizontal (.leading, .center, .trailing); for an HStack, it’s vertical (.top, .center, .bottom).
Spacer: flexible empty space
A Spacer expands to fill available space, pushing other views apart. It’s the key to many layouts.
HStack {
Text("Total")
Spacer() // pushes the price to the far right
Text("$49").bold()
}
ZStack for layering
ZStack(alignment: .bottomTrailing) {
Image("banner").resizable().scaledToFill()
Text("NEW")
.padding(6)
.background(.black.opacity(0.6))
.foregroundStyle(.white)
.padding(8)
}
Frames: controlling size
Use .frame() to set or constrain a view’s size. maxWidth: .infinity makes a view stretch to fill its parent’s width — very common for full-width buttons and cards.
Text("Full width")
.frame(maxWidth: .infinity)
.padding()
.background(.purple)
.foregroundStyle(.white)
Scrolling content
If content might exceed the screen, wrap it in a ScrollView:
ScrollView {
VStack(spacing: 12) {
ForEach(0..<20) { i in Text("Row \(i)") }
}
.padding()
}
GeometryReader (advanced, use sparingly)
When you need to know the available size (e.g. to make something half the screen wide), GeometryReader gives you the dimensions. It’s powerful but can complicate layouts, so reach for stacks, Spacer and .frame first.
Common mistakes
- Confusing spacing (gap between children) with alignment (cross-axis position).
- Forgetting
.resizable()on images (they show at full natural size). - Overusing
GeometryReaderwhen aSpaceror.frame(maxWidth:)would do.
Summary: Compose layouts with VStack/HStack/ZStack, control gaps withspacingand cross-axis position withalignment, fill space withSpacerand.frame(maxWidth:.infinity), and wrap long content in aScrollView.