iosswiftuimultidatepicker

MultiDatePicker issue while selecting more than 4 months interval in SwiftUI


I am new in SwiftUI. I am trying to implementing MultiDatePicker where user can select minimum data and maximum date in calendar. Once a dates are selected, all the dates which are comes in selected date range should be automatically selected.

Here is my code.

import SwiftUI

struct count: 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)
    }
}

#Preview {
    count()
}

My problem : It is working like a charm when user select the range within the 4 months but if user select the maximum date after the 4th month then it will not work. i.e. if user select from 1st January to 10th January it is working, it is working even user select from 1st January to any date till April. Once user select the to date that beyond the May and so then it is not working.

Attaching GIF file here for better understanding.

enter image description here

enter image description here


Solution

  • I was able to reproduce the problem when running on an iPhone 15 simulator with iOS 17.5.

    I then tried a test by taking out all the setter logic in your computed binding, reducing it to:

    Binding {
        dates
    } set: { newValue in
        dates = newValue
    }
    

    This leaves the MultiDatePicker working in the native, default way.

    I then selected every date manually, from today onwards. Today is 06 Jan. When you get to May, the date picker behaves unreliably and starts dropping dates.

    My conclusion is that MultiDatePicker is buggy on iOS 17 and cannot handle a large set of dates reliably.

    The good news is, it seems to work on iOS 18.

    To resolve your problem, I think you need to consider using a custom date selector that is better suited to supporting a range of dates. Ideally, you should only need to supply the start date and end date and not have to fill all the dates in-between.