iosswiftswift4fscalendar

How to Select Range - FSCalendar in Swift?


I am trying to use FSCalendar in one of my project to selecting range of dates.

What I found is this library have Swift version however range selection version is only available with Objective-C. So what I tried to make is using bridging, however I am unable to use the RangePickerViewController in Swift.

Did anyone implemented this library for Swift for using date range? (e.x. I want to select 2 dates as range for flight app where I am select Departure & Return flight dates.)


Solution

  • Although FSCalendar does not directly supports range selection, it does allow multiple selections, which means that at some point you would be able to handle the range selection by yourself.

    So, in the viewDidLoad() you have to make sure that you set the calendar allowsMultipleSelection property to true:

    private weak var calendar: FSCalendar!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        calendar.allowsMultipleSelection = true
    }
    

    The view controller should conform to FSCalendarDelegate protocol for handling the logic of selecting/deselecting a range.

    What we need so far is to declare two dates for the range (the starting date and the ending date):

    // first date in the range
    private var firstDate: Date?
    // last date in the range
    private var lastDate: Date?
    

    also an array of dates to hold value dates between firstDate and lastDate:

    private var datesRange: [Date]?
    

    and then implement the didSelect date and the didDeselect date delegate methods as:

    func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
        // nothing selected:
        if firstDate == nil {
            firstDate = date
            datesRange = [firstDate!]
            
            print("datesRange contains: \(datesRange!)")
            
            return
        }
        
        // only first date is selected:
        if firstDate != nil && lastDate == nil {
            // handle the case of if the last date is less than the first date:
            if date <= firstDate! {
                calendar.deselect(firstDate!)
                firstDate = date
                datesRange = [firstDate!]
                
                print("datesRange contains: \(datesRange!)")
                
                return
            }
            
            let range = datesRange(from: firstDate!, to: date)
    
            lastDate = range.last
            
            for d in range {
                calendar.select(d)
            }
            
            datesRange = range
            
            print("datesRange contains: \(datesRange!)")
            
            return
        }
        
        // both are selected:
        if firstDate != nil && lastDate != nil {
            for d in calendar.selectedDates {
                calendar.deselect(d)
            }
            
            lastDate = nil
            firstDate = nil
            
            datesRange = []
            
            print("datesRange contains: \(datesRange!)")
        }
    }
    
    func calendar(_ calendar: FSCalendar, didDeselect date: Date, at monthPosition: FSCalendarMonthPosition) {
        // both are selected:
        
        // NOTE: the is a REDUANDENT CODE:
        if firstDate != nil && lastDate != nil {
            for d in calendar.selectedDates {
                calendar.deselect(d)
            }
            
            lastDate = nil
            firstDate = nil
            
            datesRange = []
            print("datesRange contains: \(datesRange!)")
        }
    }
    

    What about datesRange method?

    I did not mention it in my answer for the purpose of making it shorter, all you have to do is to copy-paste it from this answer.

    Output:

    enter image description here