Swift中FSCalendar范围选择自定义单元格样式实现求助
解决FSCalendar范围选中等问题的实现方案
1. 完善自定义RangePickerCell的状态逻辑
确保你的RangePickerCell继承自FSCalendarCell,并实现起始、结束、中间日期的UI区分逻辑,同时处理Cell复用的状态重置:
class RangePickerCell: FSCalendarCell { // 中间日期的阴影填充图层 lazy var middleRangeLayer: CAShapeLayer = { let layer = CAShapeLayer() layer.fillColor = UIColor.systemGray5.cgColor self.contentView.layer.insertSublayer(layer, below: self.titleLabel.layer) return layer }() override func layoutSubviews() { super.layoutSubviews() middleRangeLayer.frame = CGRect(x: 0, y: bounds.height/4, width: bounds.width, height: bounds.height/2) } // 重置状态,避免复用残留 override func prepareForReuse() { super.prepareForReuse() middleRangeLayer.isHidden = true backgroundColor = .clear layer.cornerRadius = 0 layer.maskedCorners = [] } // 配置起始日期样式 func setupAsStartDate() { middleRangeLayer.isHidden = true backgroundColor = UIColor.systemBlue layer.cornerRadius = bounds.height/2 layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner] } // 配置结束日期样式 func setupAsEndDate() { middleRangeLayer.isHidden = true backgroundColor = UIColor.systemBlue layer.cornerRadius = bounds.height/2 layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner] } // 配置中间日期样式 func setupAsMiddleDate() { backgroundColor = .clear middleRangeLayer.isHidden = false } // 配置正常日期样式 func setupAsNormal() { backgroundColor = .clear middleRangeLayer.isHidden = true } }
2. 在CalendarViewController中注册并复用Cell
在视图加载阶段完成自定义Cell的注册,同时设置日历的基础配置:
override func viewDidLoad() { super.viewDidLoad() // 注册自定义Cell calendar.register(RangePickerCell.self, forCellReuseIdentifier: "RangePickerCell") // 开启多选(范围选择依赖此设置) calendar.allowsMultipleSelection = true calendar.scope = .month // 设置代理 calendar.dataSource = self calendar.delegate = self }
实现FSCalendarDataSource的Cell复用方法,每次复用都重新配置Cell状态:
func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { let cell = calendar.dequeueReusableCell(withIdentifier: "RangePickerCell", for: date, at: position) as! RangePickerCell // 根据日期状态配置Cell configureCell(cell, for: date) return cell }
3. 处理范围选择逻辑与状态更新
在ViewController中维护起始、结束日期变量,实现正确的范围选择逻辑,避免随机选中异常:
private var startDate: Date? private var endDate: Date? // 处理日期选中事件 func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) { guard let start = startDate else { startDate = date return } if endDate == nil { // 确保结束日期不早于起始日期 if date >= start { endDate = date // 选中中间所有日期 let intervalDates = getDatesBetween(start, end: date) intervalDates.forEach { calendar.select($0) } } else { // 若选了更早的日期,重置起始/结束 calendar.deselect(start) endDate = start startDate = date } } else { // 重新选择范围,清空之前的选中状态 calendar.deselect(startDate!) calendar.deselect(endDate!) getDatesBetween(startDate!, end: endDate!).forEach { calendar.deselect($0) } startDate = date endDate = nil } calendar.reloadData() } // 辅助方法:获取两个日期之间的所有日期 private func getDatesBetween(_ start: Date, end: Date) -> [Date] { var dates: [Date] = [] var current = start let calendar = Calendar.current while current <= end { dates.append(current) current = calendar.date(byAdding: .day, value: 1, to: current)! } return dates }
实现Cell状态配置方法,根据日期在范围中的位置设置对应样式:
private func configureCell(_ cell: RangePickerCell, for date: Date) { guard let start = startDate, let end = endDate else { cell.setupAsNormal() return } if date == start { cell.setupAsStartDate() } else if date == end { cell.setupAsEndDate() } else if date > start && date < end { cell.setupAsMiddleDate() } else { cell.setupAsNormal() } }
4. 修复随机日期选中异常
这个问题核心是Cell复用导致的状态残留,确保:
- 在
RangePickerCell的prepareForReuse方法中重置所有UI状态(背景色、图层显示、圆角等) - 每次复用Cell时,都通过
configureCell方法重新计算并设置状态,不依赖Cell的旧状态 - 范围选择逻辑中,仅选中起始、结束及中间的日期,通过
getDatesBetween方法确保日期范围的准确性,避免跨月份的误选
内容的提问来源于stack exchange,提问作者Naveen Kumar
相关产品推荐
相关产品推荐

