```html SwiftUI: How to Implement Date Picker (iOS 17+, 2026)

How to implement a date picker in SwiftUI

iOS 17+ Xcode 16+ Beginner APIs: DatePicker Updated: May 11, 2026
TL;DR

Bind a Date to DatePicker and pick a style — compact, graphical, or wheel. That's all you need for a fully functional, accessible date picker in SwiftUI.

import SwiftUI

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

    var body: some View {
        Form {
            DatePicker(
                "Select a date",
                selection: $selectedDate,
                displayedComponents: [.date]
            )
            .datePickerStyle(.compact)
        }
    }
}

#Preview {
    QuickDatePickerView()
}

Full implementation

The example below shows a realistic form scenario — booking a future appointment — with a constrained date range, a graphical calendar picker, and a separate time picker. A formatted summary string below the controls lets the user confirm their selection at a glance.

import SwiftUI

struct AppointmentFormView: View {
    @State private var appointmentDate = Date()
    @State private var appointmentTime = Date()
    @State private var showGraphical = false

    // Only allow bookings from today up to one year ahead
    private var dateRange: ClosedRange<Date> {
        let calendar = Calendar.current
        let start = calendar.startOfDay(for: Date())
        let end = calendar.date(
            byAdding: .year, value: 1, to: start
        ) ?? start
        return start...end
    }

    private var formattedSummary: String {
        let dateFmt = Date.FormatStyle().month(.wide).day().year()
        let timeFmt = Date.FormatStyle().hour().minute()
        return "\(appointmentDate.formatted(dateFmt)) at \(appointmentTime.formatted(timeFmt))"
    }

    var body: some View {
        NavigationStack {
            Form {
                // MARK: - Date section
                Section("Appointment date") {
                    Toggle("Show calendar", isOn: $showGraphical)

                    if showGraphical {
                        DatePicker(
                            "Date",
                            selection: $appointmentDate,
                            in: dateRange,
                            displayedComponents: [.date]
                        )
                        .datePickerStyle(.graphical)
                        .labelsHidden()
                        .accessibilityLabel("Appointment date calendar")
                    } else {
                        DatePicker(
                            "Date",
                            selection: $appointmentDate,
                            in: dateRange,
                            displayedComponents: [.date]
                        )
                        .datePickerStyle(.compact)
                    }
                }

                // MARK: - Time section
                Section("Appointment time") {
                    DatePicker(
                        "Time",
                        selection: $appointmentTime,
                        displayedComponents: [.hourAndMinute]
                    )
                    .datePickerStyle(.wheel)
                    .labelsHidden()
                    .frame(maxWidth: .infinity)
                    .accessibilityLabel("Appointment time")
                }

                // MARK: - Summary
                Section("Summary") {
                    Text(formattedSummary)
                        .font(.subheadline)
                        .foregroundStyle(.secondary)
                        .accessibilityLabel("Selected: \(formattedSummary)")
                }

                // MARK: - Confirm
                Section {
                    Button("Confirm appointment") {
                        // Handle booking
                    }
                    .frame(maxWidth: .infinity)
                    .buttonStyle(.borderedProminent)
                }
                .listRowBackground(Color.clear)
            }
            .navigationTitle("Book Appointment")
            .navigationBarTitleDisplayMode(.inline)
        }
    }
}

#Preview {
    AppointmentFormView()
}

How it works

  1. @State private var appointmentDate = Date() — A @State property stores the current selection. SwiftUI re-renders the view automatically whenever its value changes via the two-way $appointmentDate binding.
  2. in: dateRange — The ClosedRange<Date> computed property prevents users from picking dates in the past or more than a year ahead. SwiftUI greys out out-of-range dates automatically in graphical and wheel styles.
  3. displayedComponents: [.date] vs [.hourAndMinute] — Splitting date and time into two separate DatePicker controls (each with a single DisplayedComponents option) gives you independent layout control and cleaner VoiceOver announcements.
  4. .datePickerStyle(.graphical) — The toggle switches between .compact (a tappable label that expands inline) and .graphical (a full month calendar grid). Both are native UIKit-backed controls with zero extra dependencies.
  5. Date.FormatStyle() — iOS 15+ format styles replace the old DateFormatter. The formattedSummary computed property produces a localised, human-readable string using the device locale automatically — no DateFormatter boilerplate required.

Variants

Wheel picker with a minimum age gate

struct BirthdayPickerView: View {
    @State private var birthday = Calendar.current.date(
        byAdding: .year, value: -18, to: Date()
    ) ?? Date()

    // Latest allowed birthday: 18 years ago today
    private var maxDate: Date {
        Calendar.current.date(
            byAdding: .year, value: -18, to: Date()
        ) ?? Date()
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("Date of birth")
                .font(.headline)

            DatePicker(
                "Birthday",
                selection: $birthday,
                in: ...maxDate,          // one-sided range: past only
                displayedComponents: [.date]
            )
            .datePickerStyle(.wheel)
            .labelsHidden()
            .accessibilityLabel("Date of birth")
        }
        .padding()
    }
}

#Preview { BirthdayPickerView() }

Tinted graphical calendar

Apply .tint() to change the accent colour of the selected date circle and navigation chevrons in the graphical style — handy for matching your app's brand without any custom drawing:

DatePicker("Event date", selection: $eventDate,
           displayedComponents: [.date])
    .datePickerStyle(.graphical)
    .tint(.indigo)
    .labelsHidden()

Common pitfalls

Prompt this with Claude Code

When using Soarias or Claude Code directly to implement this:

Implement a date picker in SwiftUI for iOS 17+.
Use DatePicker with .compact and .graphical styles.
Constrain the selectable range using a ClosedRange<Date>.
Display a formatted summary below the picker using Date.FormatStyle.
Make it accessible (VoiceOver labels on all pickers).
Add a #Preview with realistic sample data.

In the Soarias Build phase, drop this prompt into a new screen scaffold so Claude Code generates the date picker alongside your existing form fields — keeping state management consistent with the rest of your view model.

Related

FAQ

Does this work on iOS 16?

Yes — DatePicker is available from iOS 13 and all three styles (.compact, .graphical, .wheel) work on iOS 16. However, the .graphical style has a known clipping bug inside Form on iOS 16.x. If you need to support iOS 16, wrap the graphical picker in a plain VStack or LazyVStack instead of a Form Section. Everything on this page targets iOS 17+ for the cleanest experience.

How do I show only specific times (e.g. every 30 minutes)?

SwiftUI's native DatePicker does not support a step interval for time selection — it allows any minute. For 15- or 30-minute intervals, the recommended approach is to replace the time picker with a Picker (wheel or menu style) populated with a computed array of Date values snapped to your desired interval. After selection, combine the chosen time with the date from your DatePicker using Calendar.current.dateComponents.

What is the UIKit equivalent?

The UIKit equivalent is UIDatePicker. You configure it with datePickerMode (.date, .time, .dateAndTime) and preferredDatePickerStyle (.compact, .inline, .wheels). In SwiftUI you'd only reach for UIDatePicker via a UIViewRepresentable wrapper if you need a feature not yet exposed — such as a custom locale override — but for virtually all use cases the native DatePicker view is the right choice on iOS 17+.

Last reviewed: 2026-05-11 by the Soarias team.

```