Skip to main content

Picker

Definition​

Picker is a SwiftUI view that allows users to select from a set of mutually exclusive options. It provides various presentation styles and supports different data types.

Basic Syntax​

Picker("Select Option", selection: $selectedOption) {
Text("Option 1").tag(1)
Text("Option 2").tag(2)
Text("Option 3").tag(3)
}

Simple String Picker​

struct ContentView: View {
@State private var selectedFruit = "Apple"
let fruits = ["Apple", "Banana", "Orange", "Grape"]

var body: some View {
VStack {
Picker("Select Fruit", selection: $selectedFruit) {
ForEach(fruits, id: \.self) { fruit in
Text(fruit).tag(fruit)
}
}
.pickerStyle(MenuPickerStyle())

Text("Selected: \(selectedFruit)")
}
}
}

Picker Styles​

// Wheel style (default on iOS)
Picker("Options", selection: $selection) {
ForEach(options, id: \.self) { option in
Text(option).tag(option)
}
}
.pickerStyle(WheelPickerStyle())

// Segmented style
Picker("Mode", selection: $selectedMode) {
Text("Light").tag("light")
Text("Dark").tag("dark")
}
.pickerStyle(SegmentedPickerStyle())

// Menu style
Picker("Category", selection: $selectedCategory) {
ForEach(categories, id: \.self) { category in
Text(category).tag(category)
}
}
.pickerStyle(MenuPickerStyle())

// Compact style (iOS 14+)
Picker("Size", selection: $selectedSize) {
Text("Small").tag("S")
Text("Medium").tag("M")
Text("Large").tag("L")
}
.pickerStyle(.compact)

Enum-Based Picker​

enum Priority: String, CaseIterable {
case low = "Low"
case medium = "Medium"
case high = "High"
case critical = "Critical"
}

struct PriorityPicker: View {
@State private var selectedPriority: Priority = .medium

var body: some View {
VStack {
Picker("Priority", selection: $selectedPriority) {
ForEach(Priority.allCases, id: \.self) { priority in
Text(priority.rawValue).tag(priority)
}
}
.pickerStyle(SegmentedPickerStyle())

Text("Selected Priority: \(selectedPriority.rawValue)")
.padding()
}
}
}

Custom Object Picker​

struct Person: Identifiable, Hashable {
let id = UUID()
let name: String
let age: Int
}

struct PersonPicker: View {
@State private var selectedPerson: Person?
let people = [
Person(name: "Alice", age: 30),
Person(name: "Bob", age: 25),
Person(name: "Charlie", age: 35)
]

var body: some View {
VStack {
Picker("Select Person", selection: $selectedPerson) {
Text("None").tag(Person?.none)
ForEach(people) { person in
Text("\(person.name) (\(person.age))")
.tag(Person?.some(person))
}
}
.pickerStyle(MenuPickerStyle())

if let selected = selectedPerson {
Text("Selected: \(selected.name)")
} else {
Text("No person selected")
}
}
}
}

Date Picker​

struct DatePickerExample: View {
@State private var selectedDate = Date()

var body: some View {
VStack {
// Default date picker
DatePicker("Select Date", selection: $selectedDate)

// Compact style
DatePicker("Compact Date", selection: $selectedDate)
.datePickerStyle(CompactDatePickerStyle())

// Wheel style
DatePicker("Wheel Date", selection: $selectedDate)
.datePickerStyle(WheelDatePickerStyle())

// Graphical style (iOS 14+)
DatePicker("Graphical Date", selection: $selectedDate)
.datePickerStyle(.graphical)

// Date range
DatePicker(
"Future Date",
selection: $selectedDate,
in: Date()...,
displayedComponents: .date
)
}
.padding()
}
}

Color Picker (iOS 14+)​

struct ColorPickerExample: View {
@State private var selectedColor = Color.blue

var body: some View {
VStack {
ColorPicker("Choose Color", selection: $selectedColor)

Rectangle()
.fill(selectedColor)
.frame(width: 200, height: 100)
.cornerRadius(10)

// Color picker with opacity support
ColorPicker("Color with Opacity",
selection: $selectedColor,
supportsOpacity: true)
}
.padding()
}
}

Multi-Component Picker​

struct TimePickerExample: View {
@State private var selectedHour = 12
@State private var selectedMinute = 0
@State private var selectedPeriod = "AM"

var body: some View {
HStack {
// Hour picker
Picker("Hour", selection: $selectedHour) {
ForEach(1...12, id: \.self) { hour in
Text("\(hour)").tag(hour)
}
}
.frame(width: 60)
.clipped()

Text(":")

// Minute picker
Picker("Minute", selection: $selectedMinute) {
ForEach(0..<60, id: \.self) { minute in
Text(String(format: "%02d", minute)).tag(minute)
}
}
.frame(width: 60)
.clipped()

// AM/PM picker
Picker("Period", selection: $selectedPeriod) {
Text("AM").tag("AM")
Text("PM").tag("PM")
}
.frame(width: 60)
.clipped()
}
.pickerStyle(WheelPickerStyle())
}
}

Picker in Navigation​

struct SettingsView: View {
@State private var selectedTheme = "System"
let themes = ["Light", "Dark", "System"]

var body: some View {
NavigationView {
List {
NavigationLink(destination: ThemePickerView(selectedTheme: $selectedTheme)) {
HStack {
Text("Theme")
Spacer()
Text(selectedTheme)
.foregroundColor(.secondary)
}
}
}
.navigationTitle("Settings")
}
}
}

struct ThemePickerView: View {
@Binding var selectedTheme: String
let themes = ["Light", "Dark", "System"]

var body: some View {
List {
ForEach(themes, id: \.self) { theme in
HStack {
Text(theme)
Spacer()
if theme == selectedTheme {
Image(systemName: "checkmark")
.foregroundColor(.blue)
}
}
.contentShape(Rectangle())
.onTapGesture {
selectedTheme = theme
}
}
}
.navigationTitle("Theme")
.navigationBarTitleDisplayMode(.inline)
}
}

Custom Picker Appearance​

struct CustomPicker: View {
@State private var selectedOption = 0
let options = ["Option 1", "Option 2", "Option 3"]

var body: some View {
VStack {
// Custom segmented control
Picker("Options", selection: $selectedOption) {
ForEach(0..<options.count, id: \.self) { index in
Text(options[index]).tag(index)
}
}
.pickerStyle(SegmentedPickerStyle())
.background(Color.gray.opacity(0.1))
.cornerRadius(8)

// Custom wheel picker with background
Picker("Wheel Options", selection: $selectedOption) {
ForEach(0..<options.count, id: \.self) { index in
Text(options[index])
.font(.title2)
.tag(index)
}
}
.pickerStyle(WheelPickerStyle())
.frame(height: 150)
.background(
RoundedRectangle(cornerRadius: 10)
.fill(Color.gray.opacity(0.1))
)
.clipped()
}
.padding()
}
}

Picker with Images​

struct ImagePicker: View {
@State private var selectedIcon = "star"
let icons = ["star", "heart", "person", "gear", "house"]

var body: some View {
VStack {
Picker("Select Icon", selection: $selectedIcon) {
ForEach(icons, id: \.self) { icon in
Label {
Text(icon.capitalized)
} icon: {
Image(systemName: icon)
}
.tag(icon)
}
}
.pickerStyle(MenuPickerStyle())

Image(systemName: selectedIcon)
.font(.system(size: 50))
.foregroundColor(.blue)
.padding()
}
}
}

Conditional Picker Options​

struct ConditionalPicker: View {
@State private var selectedCategory = "Electronics"
@State private var selectedItem = ""

let categories = ["Electronics", "Clothing", "Books"]

var items: [String] {
switch selectedCategory {
case "Electronics":
return ["iPhone", "MacBook", "iPad"]
case "Clothing":
return ["T-Shirt", "Jeans", "Shoes"]
case "Books":
return ["Fiction", "Non-Fiction", "Biography"]
default:
return []
}
}

var body: some View {
VStack {
Picker("Category", selection: $selectedCategory) {
ForEach(categories, id: \.self) { category in
Text(category).tag(category)
}
}
.pickerStyle(SegmentedPickerStyle())
.onChange(of: selectedCategory) { _ in
selectedItem = items.first ?? ""
}

Picker("Item", selection: $selectedItem) {
ForEach(items, id: \.self) { item in
Text(item).tag(item)
}
}
.pickerStyle(MenuPickerStyle())
.onAppear {
if selectedItem.isEmpty {
selectedItem = items.first ?? ""
}
}

Text("Selected: \(selectedCategory) - \(selectedItem)")
.padding()
}
.padding()
}
}

Best Practices​

  1. Use appropriate styles: Choose picker style based on context and space
  2. Provide clear labels: Make picker purpose obvious to users
  3. Handle empty states: Ensure pickers have default values
  4. Consider accessibility: Test with VoiceOver and other assistive technologies
  5. Use tags correctly: Ensure each option has a unique, meaningful tag
  6. Limit options: Too many options can overwhelm users
  7. Group related options: Use sections or separate pickers for different categories

Common Use Cases​

  • Settings and preferences
  • Form inputs and filters
  • Date and time selection
  • Category and type selection
  • Size and variant selection
  • Language and region selection
  • Theme and appearance options