admin管理员组

文章数量:1122846

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 = selectedDatespactMap { 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.

  • Gif when user select small date range period like January to February then it is working as expected.

  • Gif file when user select the dates more than 4 months like January to Augest September etc.

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.

  • Gif when user select small date range period like January to February then it is working as expected.

  • Gif file when user select the dates more than 4 months like January to Augest September etc.

Share Improve this question edited yesterday Bandish asked yesterday BandishBandish 152 bronze badges 3
  • It works for me running on an iPhone 16 simulator with iOS 18.2. Have you tried running on a simulator instead of Preview? What iOS version are you running on? – Benzy Neez Commented yesterday
  • @BenzyNeez I have checked with simulator and physical device too but i faced the same issue as i mentioned. I am running simulator on iOS 17.0 as i have Xcode 15.0.1., I have update the question with adding gif file. – Bandish Commented yesterday
  • @BenzyNeez You right brother I have also check with iOS 18.2 and it's working fine, again thank you so much for your swift help :) – Bandish Commented yesterday
Add a comment  | 

1 Answer 1

Reset to default 0

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.

本文标签: iosMultiDatePicker issue while selecting more than 4 months interval in SwiftUIStack Overflow