Reputation: 12079
I'm working on my first SwiftUI app, and have implemented a Form
with one of the fields being a DatePicker
. However, this value is completely optional. So, it is possible that the user doesn't enter a value at all. I'm unable to figure out, though, how I can make it so that the user doesn't need to enter the value. The code itself is rather simple:
DatePicker(selection: $expirationDate,
displayedComponents: .date) {
Text("Expiration")
}
Is there a way so that I can indicate that $expirationDate
can have a Date
or can be nil? The DatePicker
by default seems to set the value to the current Date
and I couldn't figure out a way to override that behavior. Thoughts?
Upvotes: 8
Views: 4527
Reputation: 3918
My solution is similar to bdan's solution, but in his solution, once you set the date, you cannot remove it.
Date set (with option to remove it):
This is the code:
struct DatePickerOptional: View {
let label: String
let prompt: String
let range: PartialRangeThrough<Date>
@Binding var date: Date?
@State private var hidenDate: Date = Date()
@State private var showDate: Bool = false
init(_ label: String, prompt: String, in range: PartialRangeThrough<Date>, selection: Binding<Date?>) {
self.label = label
self.prompt = prompt
self.range = range
self._date = selection
}
var body: some View {
ZStack {
HStack {
Text(label)
.multilineTextAlignment(.leading)
Spacer()
if showDate {
Button {
showDate = false
date = nil
} label: {
Image(systemName: "xmark.circle")
.resizable()
.frame(width: 16, height: 16)
.tint(.textPrimary)
}
DatePicker(
label,
selection: $hidenDate,
in: range,
displayedComponents: .date
)
.labelsHidden()
.onChange(of: hidenDate) { newDate in
date = newDate
}
} else {
Button {
showDate = true
date = hidenDate
} label: {
Text(prompt)
.multilineTextAlignment(.center)
.foregroundColor(.textPrimary)
}
.frame(width: 120, height: 34)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(Color.customPickerBackground)
)
.multilineTextAlignment(.trailing)
}
}
}
}
}
Upvotes: 6
Reputation: 41
You can add Text on top of the DatePicker that does not receive touch events, then add a white background with a prompt. This will allow the date to still be clickable.
struct DatePickerNullable: View {
let label:String
@Binding var date:Date?
@State private var hidenDate:Date = Date()
init(_ label:String, selection:Binding<Date?>){
self.label = label
self._date = selection
}
var body: some View {
GeometryReader{ proxy in
ZStack{
DatePicker(label, selection: $hidenDate, displayedComponents: .date)
if date == nil{
Text(label)
.italic()
.foregroundColor(.gray)
.frame(width: proxy.size.width - CGFloat(label.count * 8), alignment: .trailing)
.background(Color.white)
.frame(maxWidth: .infinity, alignment: .trailing)
.allowsHitTesting(false)
}
}.onChange(of: hidenDate, perform: { _ in
self.date = hidenDate
})
}
}
}
Upvotes: 4