SwiftUI Picker: Segmented, Menu, Wheel & Enum Bindings
Choose the right Picker style for the job — segmented, menu, wheel, or navigation link — and bind cleanly to a CaseIterable enum with correct .tag() usage.
A SwiftUI Picker binds a selection to a set of choices you list inside it, tagging each with .tag() so it matches the binding's type. Switch presentation with .pickerStyle: .segmented for a few options, .menu for a compact pop-up, .wheel for a spinning drum, and .navigationLink to push a selection screen. Bind it to a CaseIterable enum and iterate the cases with ForEach for type-safe options.
How a Picker binds to a selection
A Picker connects a selection binding to a list of options. Each option must carry a .tag whose value type exactly matches the binding, or the picker will silently show no selection — this is the single most common Picker bug. When you use a ForEach over Identifiable data, the id often supplies the tag automatically, but for anything else, attach .tag explicitly. Keep the tag type consistent: mixing an Int selection with String tags breaks the match.
@State private var quantity = 1
Picker("Quantity", selection: $quantity) {
ForEach(1...10, id: \.self) { n in
Text("\(n)").tag(n) // tag type Int matches $quantity
}
}Binding to a CaseIterable enum
The cleanest pickers bind to an enum. Make it String-backed, conform to CaseIterable and Identifiable, and you can iterate every case with ForEach while the compiler guarantees you handle only valid values. This pattern keeps your options and their display strings in one place and eliminates stringly-typed bugs. It also makes the selection trivially Codable for persistence in @AppStorage or a settings store.
enum Theme: String, CaseIterable, Identifiable {
case system, light, dark
var id: Self { self }
var label: String { rawValue.capitalized }
}
@State private var theme: Theme = .system
Picker("Appearance", selection: $theme) {
ForEach(Theme.allCases) { theme in
Text(theme.label).tag(theme)
}
}Choosing a picker style
The style you pick shapes the whole interaction, so match it to the number of options and the context. Segmented works best for two to four mutually exclusive choices shown inline. Menu is ideal in forms where space is tight — it collapses to the current value and expands on tap. Wheel suits ranges like time or quantity where scrubbing feels natural. NavigationLink pushes a dedicated selection list, which shines when options are numerous or need subtitles.
- .segmented — 2 to 4 inline options, always visible
- .menu — compact pop-up, great inside Forms
- .wheel — spinning drum for ranges and dates
- .navigationLink — pushes a full selection screen for long lists
- .inline — expands all options directly in a List
Picker("Sort", selection: $sort) {
ForEach(SortOrder.allCases) { Text($0.label).tag($0) }
}
.pickerStyle(.segmented) // or .menu, .navigationLink
// .navigationLink requires being inside a NavigationStackMulti-component wheel pickers
To build a picker with several spinning columns — think a custom duration picker of hours and minutes — place multiple Pickers side by side in an HStack, each with .pickerStyle(.wheel) and its own binding. SwiftUI does not offer a single multi-column Picker type, so composing wheels is the idiomatic approach. Give each wheel a frame and clip it if you need tight spacing, and label the columns for accessibility.
@State private var hours = 0
@State private var minutes = 0
HStack(spacing: 0) {
Picker("Hours", selection: $hours) {
ForEach(0..<24, id: \.self) { Text("\($0) h").tag($0) }
}
.pickerStyle(.wheel)
Picker("Minutes", selection: $minutes) {
ForEach(0..<60, id: \.self) { Text("\($0) m").tag($0) }
}
.pickerStyle(.wheel)
}
.frame(height: 160)Pickers inside Forms and iOS 26 notes
Dropped into a Form or List, a Picker automatically adopts the compact .menu style with a trailing chevron, which is the expected look for settings screens. You rarely need to set the style explicitly there. On iOS 26, form rows and menu pickers pick up the Liquid Glass material along with the rest of the system, so a picker built with the standard styles updates its look for free — no code changes required beyond running on the new SDK.
Form {
Section("Preferences") {
Picker("Theme", selection: $theme) {
ForEach(Theme.allCases) { Text($0.label).tag($0) }
}
// Renders as a compact menu row with a chevron
}
}Ship your SwiftUI app in 5 emails
A free 5-part course on the parts that actually stall launches: paywall, auth, onboarding, App Store review, and pricing. No fluff, unsubscribe anytime.
Settings screens, solved
The Swift Kit's settings module uses enum-bound pickers for theme and AI provider, so you get correctly tagged, persistable choices without the tag-matching headaches.
Rather have it done for you? I set up and ship your app from $499. See how
Frequently Asked Questions
How do I make a segmented Picker in SwiftUI?
Why is my SwiftUI Picker selection not updating?
How do I bind a SwiftUI Picker to an enum?
How do I build a multi-column wheel Picker in SwiftUI?
When should I use .menu versus .navigationLink Picker style in SwiftUI?
Keep exploring
Stop writing settings plumbing
The Swift Kit is a $99 one-time SwiftUI boilerplate with a full settings screen, auth, and paywalls built in — lifetime updates and a 14-day refund.
Get The Swift Kit — $99One-time purchase · Lifetime updates · 14-day refund