SwiftUI Layout (watchOS)
Glanceable design: users interact with Apple Watch for 1-2 seconds. Every view must communicate its purpose instantly.
Watch Screen Constraints
- Screen sizes: 40mm (162pt), 41mm (176pt), 44mm (184pt), 45mm (198pt), 49mm Ultra (205pt)
- Always design for the smallest screen, let larger screens breathe
- No landscape orientation — always portrait
- No split views, no multi-column layouts
Layout Principles
Vertical Stacking
// Good - simple vertical stack, fills naturally
VStack(spacing: 8) {
Text("Heart Rate")
.font(AppTheme.Fonts.caption2)
.foregroundStyle(.secondary)
Text("72")
.font(AppTheme.Fonts.largeTitle)
Text("BPM")
.font(AppTheme.Fonts.caption)
.foregroundStyle(.secondary)
}
Full-Width Elements
// Good - buttons fill width on watch
Button("Start Workout") {
startWorkout()
}
.buttonStyle(.borderedProminent)
// Buttons naturally fill width on watchOS — no .frame(maxWidth:) needed
containerRelativeFrame (preferred over GeometryReader)
Image("chart")
.resizable()
.containerRelativeFrame(.horizontal) { width, _ in
width * 0.9
}
.aspectRatio(contentMode: .fit)
View Structure
Prefer Modifiers Over Conditional Views
// Good - same view, different states
SomeView()
.opacity(isVisible ? 1 : 0)
// Avoid - creates/destroys view identity
if isVisible {
SomeView()
}
Extract Subviews Into Separate Structs
// Good - separate struct, SwiftUI can skip body when inputs unchanged
struct MetricDisplay: View {
let value: Int
let unit: String
var body: some View {
VStack {
Text("\(value)")
.font(AppTheme.Fonts.title2)
Text(unit)
.font(AppTheme.Fonts.caption2)
.foregroundStyle(.secondary)
}
}
}
What NOT to Use on watchOS
- No
GeometryReader— usecontainerRelativeFrameor let stacks fill naturally - No
UIScreen.main.bounds— doesn't exist on watchOS - No size classes — watch has one size class
- No
NavigationSplitView— watch is single-column only - No adaptive grids with many columns — use simple
VStackor single-columnList - No
.frame(maxWidth: 700)readability constraints — the screen is already small - No
AnyLayoutswitching — there's only one layout direction on watch
Layout Rules
- Keep views shallow — 2-3 levels of nesting maximum
- Use
Listfor scrollable content,ScrollViewfor custom layouts - Large text (
.title,.largeTitle) for primary information - Small text (
.caption,.caption2) for labels and secondary info - Minimum tap target: 44pt (Apple requirement, critical on small screen)
- Use
@ScaledMetricfor custom dimensions that respect Dynamic Type