I am new in SwiftUI project where I need to implement a feature using MultiDatePicker that allows a user to select two dates only. Once a date are selected, all the dates which are comes in selected date range should be automatically selected.
Here I am attaching screen shot for better understanding.
In above case user select from 27th December 2024 to 31th December 2024
So is it possible where the range of date between 27th and 31th December 2024 are getting selected automatically without select manually or tap on it. Expectation like
Thanks in advance
Code Edited
struct count: View {
@State private var dates: Set<DateComponents> = []
var datesBinding: Binding<Set<DateComponents>> {
Binding {
return dates
} set: { newValue in
self.dates = newValue
}
}
var body: some View {
return VStack(spacing: 50){
MultiDatePicker("Select dates", selection: datesBinding)
.frame(height: 300)
}
.padding()
}
}
Here I edit the where I am trying to return the range of dates but unfortunately i have no idea how to implement the expected functionality.
Adding GIF file for better understanding
One way to respond to user interaction would be to add an .onChange
callback to the date picker. However, using a computed binding (as you are doing in your example) is another way.
The advantage of using a computed binding is that changes to the set of DateComponents
can be intercepted and modified, without the update causing a recursive call, as would be the case with .onChange
. So using a computed binding is perhaps a better approach than using .onChange
.
Here is how you might want to intercept changes to the set of dates:
If the new set of dates is empty, the change can be adopted. This would be the case when the user taps on the same date twice.
If the size of the date set has grown larger and there are now two dates, any gaps between the dates should be filled.
Otherwise, the date that was just added or just removed from the set should be identified and set as a single date. This becomes the start of a new range.
In order to convert between DateComponents
and Date
, it is important to use the same set of Calendar.Component
as the picker is using. You will see from the post MultiDatePicker onChange not called if selection is set programmatically that this set consists of:
[.calendar, .era, .year, .month, .day]
So here is an example implementation that works as described above:
struct ContentView: View {
@Environment(\.calendar) var calendar
@State private var dates: Set<DateComponents> = []
let datePickerComponents: Set<Calendar.Component> = [.calendar, .era, .year, .month, .day]
var datesBinding: Binding<Set<DateComponents>> {
Binding {
dates
} set: { newValue in
if newValue.isEmpty {
dates = newValue
} else if newValue.count > dates.count {
if newValue.count == 1 {
dates = newValue
} else if newValue.count == 2 {
dates = filledRange(selectedDates: newValue)
} else if let firstMissingDate = newValue.subtracting(dates).first {
dates = [firstMissingDate]
} else {
dates = []
}
} else if let firstMissingDate = dates.subtracting(newValue).first {
dates = [firstMissingDate]
} else {
dates = []
}
}
}
var body: some View {
VStack(spacing: 50){
MultiDatePicker("Select dates", selection: datesBinding)
.frame(height: 300)
}
.padding()
}
private func filledRange(selectedDates: Set<DateComponents>) -> Set<DateComponents> {
let allDates = selectedDates.compactMap { calendar.date(from: $0) }
let sortedDates = allDates.sorted()
var datesToAdd = [DateComponents]()
if let first = sortedDates.first, let last = sortedDates.last {
var date = first
while date < last {
if let nextDate = calendar.date(byAdding: .day, value: 1, to: date) {
if !sortedDates.contains(nextDate) {
let dateComponents = calendar.dateComponents(datePickerComponents, from: nextDate)
datesToAdd.append(dateComponents)
}
date = nextDate
} else {
break
}
}
}
return selectedDates.union(datesToAdd)
}
}
In your animated gif, the range of dates was shown with a solid background. I think you would need to implement your own custom date picker to achieve this effect, the native MultiDatePicker
always shows the selection as a set of individual dates.