Fscalendar: is it possible to select dates in the range in recent version of fscalender?

Created on 8 Mar 2017  ·  3Comments  ·  Source: WenchaoD/FSCalendar

The following informations are requested in a bug report

  • A brief bug description.
  • Stack trace.
  • Integration method.(manually/cocoapods/carthage)
  • Full steps to reproduce.
  • Device modal and iOS version. e.g. iPhone 6s iOS9.1
  • Xcode version. e.g. Xcode 8.1
  • FSCalendar version. e.g. FSCalenda 2.5.1
  • Does this happen in the demo project? Which one? Or a link to another demo project.

如果在使用过程中遇到问题,请提供以下信息以便于尽快将问题修复

  • 简单描述一下问题。
  • 控制台输出的堆栈跟踪(如果有的话)。
  • 安装方式. 如手动安装、cocoapods安装、或者通过carthage安装
  • 能够重现此问题的完整步骤。
  • 设备型号及版本。 例如: iPhone6s iOS9.1
  • 使用的Xcode版本。 例如: Xcode8.1
  • FSCalendar版本. 例如: FSCalendar 2.5.1
  • 是否能在demo中重现,以及在哪个demo中重现?或者附加一个重现问题的demo链接。

Most helpful comment

Here's a workaround I created for this limitation. In your view controller, create three variables and set the delegate:

1) var startDate: Date?
2) var endDate: Date?
3) selectedDateArray: [Date] = [] // empty array
4) calendarView.delegate = self
5) Implement the FSCalendarDelegate methods for didSelect and didDeselect.

Here's the code:

  var selectedDateArray: [Date] = [] {
    didSet {
      // sort the array
      selectedDateArray = calendarView.selectedDates.sorted()

      switch selectedDateArray.count {
      case 0:
        startDate = nil
        endDate = nil
      case 1:
        startDate = selectedDateArray.first
        endDate = nil
      case _ where selectedDateArray.count > 1:
        startDate = selectedDateArray.first
        endDate = selectedDateArray.last

        var nextDay = Calendar.current.date(byAdding: .day, value: 1, to: startDate!)
        while nextDay!.startOfDay <= endDate! {
          calendarView.select(nextDay)
          nextDay = Calendar.current.date(byAdding: .day, value: 1, to: nextDay!)

        }
      default:
        return
      }
    }
  }

  var startDate: Date? {
    didSet {
      startDate = startDate?.startOfDay
    }
  }

  var endDate: Date? {
    didSet {
      endDate = endDate?.endOfDay
    }
  }

extension ViewController: FSCalendarDelegate {
  func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
    if date.isAfterDate(Date().endOfDay!) {
      calendar.deselect(date)
    } else {
      selectedDateArray.append(date)
    }
  }

  func calendar(_ calendar: FSCalendar, didDeselect date: Date, at monthPosition: FSCalendarMonthPosition) {
    if calendar.selectedDates.count > 2 {
      let datesToDeselect: [Date] = calendar.selectedDates.filter{ $0 > date }
      datesToDeselect.forEach{ calendar.deselect($0) }
      calendar.select(date) // adds back the end date that was just deselected so it matches selectedDateArray
    }
    selectedDateArray = selectedDateArray.filter{ $0 < date }
    selectedDateArray.forEach{calendarView.select($0)}
  }
}

Here are the Date extension methods I used:

extension Date {

  func isSameDate(_ comparisonDate: Date) -> Bool {
    let order = Calendar.current.compare(self, to: comparisonDate, toGranularity: .day)
    return order == .orderedSame
  }

  func isBeforeDate(_ comparisonDate: Date) -> Bool {
    let order = Calendar.current.compare(self, to: comparisonDate, toGranularity: .day)

    return order == .orderedAscending
  }

  func isAfterDate(_ comparisonDate: Date) -> Bool {
    let order = Calendar.current.compare(self, to: comparisonDate, toGranularity: .day)
    return order == .orderedDescending
  }

  var startOfDay: Date {
    return Calendar.current.startOfDay(for: self)
  }

  var endOfDay: Date? {
    var components = DateComponents()
    components.day = 1
    components.second = -1
    return Calendar.current.date(byAdding: components, to: startOfDay)
  }
}

All 3 comments

No. Just call [calendar selectDate:] for each date.

Here's a workaround I created for this limitation. In your view controller, create three variables and set the delegate:

1) var startDate: Date?
2) var endDate: Date?
3) selectedDateArray: [Date] = [] // empty array
4) calendarView.delegate = self
5) Implement the FSCalendarDelegate methods for didSelect and didDeselect.

Here's the code:

  var selectedDateArray: [Date] = [] {
    didSet {
      // sort the array
      selectedDateArray = calendarView.selectedDates.sorted()

      switch selectedDateArray.count {
      case 0:
        startDate = nil
        endDate = nil
      case 1:
        startDate = selectedDateArray.first
        endDate = nil
      case _ where selectedDateArray.count > 1:
        startDate = selectedDateArray.first
        endDate = selectedDateArray.last

        var nextDay = Calendar.current.date(byAdding: .day, value: 1, to: startDate!)
        while nextDay!.startOfDay <= endDate! {
          calendarView.select(nextDay)
          nextDay = Calendar.current.date(byAdding: .day, value: 1, to: nextDay!)

        }
      default:
        return
      }
    }
  }

  var startDate: Date? {
    didSet {
      startDate = startDate?.startOfDay
    }
  }

  var endDate: Date? {
    didSet {
      endDate = endDate?.endOfDay
    }
  }

extension ViewController: FSCalendarDelegate {
  func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
    if date.isAfterDate(Date().endOfDay!) {
      calendar.deselect(date)
    } else {
      selectedDateArray.append(date)
    }
  }

  func calendar(_ calendar: FSCalendar, didDeselect date: Date, at monthPosition: FSCalendarMonthPosition) {
    if calendar.selectedDates.count > 2 {
      let datesToDeselect: [Date] = calendar.selectedDates.filter{ $0 > date }
      datesToDeselect.forEach{ calendar.deselect($0) }
      calendar.select(date) // adds back the end date that was just deselected so it matches selectedDateArray
    }
    selectedDateArray = selectedDateArray.filter{ $0 < date }
    selectedDateArray.forEach{calendarView.select($0)}
  }
}

Here are the Date extension methods I used:

extension Date {

  func isSameDate(_ comparisonDate: Date) -> Bool {
    let order = Calendar.current.compare(self, to: comparisonDate, toGranularity: .day)
    return order == .orderedSame
  }

  func isBeforeDate(_ comparisonDate: Date) -> Bool {
    let order = Calendar.current.compare(self, to: comparisonDate, toGranularity: .day)

    return order == .orderedAscending
  }

  func isAfterDate(_ comparisonDate: Date) -> Bool {
    let order = Calendar.current.compare(self, to: comparisonDate, toGranularity: .day)
    return order == .orderedDescending
  }

  var startOfDay: Date {
    return Calendar.current.startOfDay(for: self)
  }

  var endOfDay: Date? {
    var components = DateComponents()
    components.day = 1
    components.second = -1
    return Calendar.current.date(byAdding: components, to: startOfDay)
  }
}

Awesome. Thanks.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

adavan2107 picture adavan2107  ·  5Comments

thaoth58 picture thaoth58  ·  3Comments

denikaev picture denikaev  ·  5Comments

garudaonekh picture garudaonekh  ·  4Comments

ghost picture ghost  ·  5Comments