SwiftUI gives us a dedicated picker type called DatePicker
that can be bound to a date property. Yes, Swift has a dedicated type for working with dates, and it’s called – unsurprisingly – Date
.
So, to use it you’d start with an @State
property such as this:
@State private var wakeUp = Date()
You could then bind that to a date picker like this:
var body: some View {
DatePicker("Please enter a date", selection: $wakeUp)
}
Try running that in the simulator so you can see how it looks. You should see a spinning wheel with days and times, plus the “Please enter a date” label on the left.
Now, you might think that label looks ugly, and try replacing it with this:
DatePicker("", selection: $wakeUp)
But if you do that you now have two problems: the date picker still makes space for a label even though it’s empty, and now users with the screen reader active (more familiar to us as VoiceOver) won’t have any idea what the date picker is for.
There are two alternatives, both of which solve the problem.
First, we can wrap the DatePicker
in a Form
:
var body: some View {
Form {
DatePicker("Please enter a date", selection: $wakeUp)
}
}
Just like a regular Picker
this changes the way SwiftUI renders the view. We don’t get a new view being pushed onto a NavigationView
this time, though; instead we get a single list row that folds out into a date picker when tapped.
This looks really nice, and combines the clean simplicity of forms with the familiar, wheel-based user interface of date pickers. Sadly, right now there are occasionally some glitches with the way these pickers are shown; we’ll get onto that later.
Rather than using forms, an alternative is to use the labelsHidden()
modifier, like this:
var body: some View {
DatePicker("Please enter a date", selection: $wakeUp)
.labelsHidden()
}
That still includes the original label so screen readers can use it for VoiceOver, but now they aren’t visible onscreen any more – the date picker will take up all horizontal space on the screen.
Date pickers provide us with a couple of configuration options that control how they work. First, we can use displayedComponents
to decide what kind of options users should see:
.date
users see month, day, and year..hourAndMinute
users see just the hour and minute components.So, we can select a precise time like this:
DatePicker("Please enter a time", selection: $wakeUp, displayedComponents: .hourAndMinute)
Finally, there’s an in
parameter that works just the same as with Stepper
: we can provide it with a date range, and the date picker will ensure the user can’t select beyond it.
Now, we’ve been using ranges for a while now, and you’re used to seeing things like 1 ... 5
or 0 ..< 10
, but we can also use Swift dates with ranges. For example:
// when you create a new Date instance it will be set to the current date and time
let now = Date()
// create a second Date instance set to one day in seconds from now
let tomorrow = Date().addingTimeInterval(86400)
// create a range from those two
let range = now ... tomorrow
That’s really useful with DatePicker
, but there’s something even better: Swift lets us form one-sided ranges – ranges where we specify either the start or end but not both, leaving Swift to infer the other side.
For example, we could create a date picker like this:
DatePicker("Please enter a date", selection: $wakeUp, in: Date()...)
That will allow all dates in the future, but none in the past – read it as “from the current date up to anything.”
SPONSORED From January 26th to 31st you can join a FREE crash course for iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a senior developer!
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.