For purposes of this discussion, Today means December 17, 2022
The issue is that Date.now is not equal to Today
I'm in US East Coast Time Zone... if I add a button to print(Date.now) and tap it, I see this in the debug console:
2022-12-17 14:08:52 +0000
if I tap it again 4-seconds later, I see this:
2022-12-17 14:08:56 +0000
Those two dates are not equal.
So, let's find out what the MultiDatePicker is using for it's selection.
Change your MultiDatePicker to this:
MultiDatePicker("Select dates", selection: $selectedDates)
.frame(height: UIScreen.main.bounds.width)
.onChange(of: selectedDates) { _ in
print("onChange")
selectedDates.forEach { d in
print(d)
}
print()
self.onChangeCounter += 1
}
If I run the app and select Dec 14, 19 and 8, I see this:
onChange
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 14 isLeapMonth: false
onChange
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 14 isLeapMonth: false
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 19 isLeapMonth: false
onChange
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 14 isLeapMonth: false
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 19 isLeapMonth: false
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 8 isLeapMonth: false
Now, I de-select the 19th, and I see this:
onChange
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 14 isLeapMonth: false
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 8 isLeapMonth: false
The 19th was correctly removed from the Set.
Now, I tap your "Select today" button, and I see this:
onChange
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 14 isLeapMonth: false
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 8 isLeapMonth: false
calendar: gregorian (current) timeZone: America/New_York (fixed (equal to current)) era: 1 year: 2022 month: 12 day: 17 hour: 9 minute: 24 second: 11 nanosecond: 339460015 weekday: 7 weekdayOrdinal: 3 quarter: 0 weekOfMonth: 3 weekOfYear: 51 yearForWeekOfYear: 2022 isLeapMonth: false
As we can see, these two lines:
let todayDatecomponents = calendar.dateComponents(in: calendar.timeZone, from: Date.now)
selectedDates.insert(todayDatecomponents)
Insert a DateComponents object with a lot more detail.
If I tap "Select today" again, I get this:
onChange
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 8 isLeapMonth: false
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 14 isLeapMonth: false
calendar: gregorian (current) timeZone: America/New_York (fixed (equal to current)) era: 1 year: 2022 month: 12 day: 17 hour: 9 minute: 24 second: 11 nanosecond: 339460015 weekday: 7 weekdayOrdinal: 3 quarter: 0 weekOfMonth: 3 weekOfYear: 51 yearForWeekOfYear: 2022 isLeapMonth: false
calendar: gregorian (current) timeZone: America/New_York (fixed (equal to current)) era: 1 year: 2022 month: 12 day: 17 hour: 9 minute: 26 second: 39 nanosecond: 866878032 weekday: 7 weekdayOrdinal: 3 quarter: 0 weekOfMonth: 3 weekOfYear: 51 yearForWeekOfYear: 2022 isLeapMonth: false
Now selectedDates contains two "today dates" ... 2-minutes and 28-seconds apart.
When I tap the 17th on the calendar, there is no matching date in that set to remove... so when the calendar refreshes (such as when I select another date), the 17th still shows as selected.
So, let's change the programmatically inserted DateComponents to match the calendar's data:
let todayDatecomponents = calendar.dateComponents([.calendar, .era, .year, .month, .day], from: Date.now)
selectedDates.insert(todayDatecomponents)
Now when we tap 17 on the calendar it will be de-selected and the matching object selectedDates will be removed.
Here's how I modified your code to debug:
import SwiftUI
@available(iOS 16.0, *)
struct MultiDateView: View {
@Environment(\.calendar) private var calendar
@State private var selectedDates: Set<DateComponents> = []
@State private var onChangeCounter = 0
var body: some View {
VStack {
MultiDatePicker("Select dates", selection: $selectedDates)
.frame(height: UIScreen.main.bounds.width)
.onChange(of: selectedDates) { _ in
print("onChange")
selectedDates.forEach { d in
print(d)
}
print()
self.onChangeCounter += 1
}
Button("Select today") {
let todayDatecomponents = calendar.dateComponents(in: calendar.timeZone, from: Date.now)
selectedDates.insert(todayDatecomponents)
}
.foregroundColor(.white)
.frame(minWidth: 150)
.padding()
.background(Color.accentColor)
.cornerRadius(20)
Button("Select today the right way") {
let todayDatecomponents = calendar.dateComponents([.calendar, .era, .year, .month, .day], from: Date.now)
selectedDates.insert(todayDatecomponents)
}
.foregroundColor(.white)
.frame(minWidth: 150)
.padding()
.background(Color.green)
.cornerRadius(20)
HStack {
Text("onChangeCounter")
Spacer()
Text(String(onChangeCounter))
}
.padding()
Button("Print Date.now in debug console") {
print("debug")
print("debug:", Date.now)
print()
}
.foregroundColor(.white)
.frame(minWidth: 150)
.padding()
.background(Color.red)
.cornerRadius(20)
Spacer()
}
}
}
@available(iOS 16.0, *)
struct MultiDateView_Previews: PreviewProvider {
static var previews: some View {
MultiDateView()
}
}