| name | components |
| description | watchOS UI components: watch-sized buttons, grouped lists, toggles, pickers, progress, empty states. Use when working on shared watchOS patterns related to components. |
Component Patterns (watchOS)
BUTTON HIERARCHY (watch-sized):
| Level | Style | Use Case |
|---|
| Primary | .borderedProminent | Main action (Start, Save) |
| Secondary | .bordered | Alternative (Cancel, Edit) |
| Destructive | .borderedProminent + .tint(.red) | Delete, Remove |
- Buttons naturally fill width on watchOS — no
.frame(maxWidth: .infinity) needed
- Use
.controlSize(.mini) or .controlSize(.small) for compact button groups
- ONE
.borderedProminent per screen
- ALWAYS use
Button() — never .onTapGesture for actions
Button("Start") { start() }
.buttonStyle(.borderedProminent)
HStack {
Button("Skip") { skip() }
.buttonStyle(.bordered)
.controlSize(.small)
Button("Next") { next() }
.buttonStyle(.borderedProminent)
.controlSize(.small)
}
LIST PATTERNS:
List {
Section("Today") {
ForEach(todayItems) { item in
NavigationLink(value: item) {
HStack {
Image(systemName: item.icon)
Text(item.title)
Spacer()
Text(item.value)
.foregroundStyle(.secondary)
}
}
}
}
Section("Settings") {
Toggle("Notifications", isOn: $notifications)
}
}
TOGGLE:
Section("Preferences") {
Toggle("Sound", isOn: $soundEnabled)
Toggle("Haptics", isOn: $hapticsEnabled)
}
PICKER:
Picker("Speed", selection: $speed) {
Text("Slow").tag(Speed.slow)
Text("Medium").tag(Speed.medium)
Text("Fast").tag(Speed.fast)
}
Picker("Category", selection: $category) {
ForEach(categories) { cat in
Text(cat.name).tag(cat)
}
}
PROGRESS:
ProgressView(value: progress, total: 1.0)
.progressViewStyle(.circular)
ProgressView(value: 0.6)
ProgressView("Loading...")
GAUGE (watch-native):
Gauge(value: heartRate, in: 40...200) {
Text("BPM")
} currentValueLabel: {
Text("\(Int(heartRate))")
} minimumValueLabel: {
Text("40")
} maximumValueLabel: {
Text("200")
}
.gaugeStyle(.accessoryCircular)
EMPTY STATES:
ContentUnavailableView(
"No Workouts",
systemImage: "figure.run",
description: Text("Start a workout to see it here")
)
DATE/TIME DISPLAY:
Text(Date.now, style: .time)
.font(AppTheme.Fonts.title2)
Text(Date.now, style: .relative)
.font(AppTheme.Fonts.caption)
NOT AVAILABLE ON watchOS:
- No card patterns with shadows (watch doesn't use card-based UI)
- No
.textFieldStyle(.roundedBorder) — watch text input is system-driven
- No
.redacted(reason: .placeholder) skeleton loading (too complex for watch)
- No full-width
.controlSize(.large) buttons — watch buttons fill width by default
- No
.popover presentations
RULES:
- Keep components minimal — watch screen is ~40mm
- Use
List with sections as the primary content container
- Gauge is the watch-native way to show metrics — prefer it over custom progress views
- Text input on watch is limited — prefer selection (Picker, Toggle) over TextField
- Use SF Symbols at appropriate sizes —
.font(.title3) for icons in lists