如何在选中的UICollectionView单元格上显示计数标签?
iOS 月历组件预选中时段+右上角序号标签实现方案
整个实现不需要改动现有CalendarCollectionView的核心滚动、月切换逻辑,只需要补充数据映射、单元格自定义、渲染逻辑三个模块即可:
预选中日期预处理
不要在列表滚动渲染时重复做排序、匹配计算,提前把预选中的PreselectedPeriod数据处理好:- 写一个日期归一化方法,把所有日期的时分秒清零,避免同一天因为时间戳精度、时分秒差异导致匹配失败
- 把所有预选中日期按自然天升序排列
- 生成
[Date: Int]类型的键值对,key是归一化后的日期,value是对应要显示的序号(从1开始计数)
参考代码:
let defaultCalendar = Calendar.current // 日期归一化:只保留年月日维度 func stripTime(from date: Date) -> Date { let comps = defaultCalendar.dateComponents([.year, .month, .day], from: date) return defaultCalendar.date(from: comps)! } // 提前生成索引映射 let sortedSelectedDates = preselectedPeriod.sorted() var date2IndexMap = [Date: Int]() for (offset, date) in sortedSelectedDates.enumerated() { date2IndexMap[stripTime(from: date)] = offset + 1 }自定义日期单元格
在你现有日期单元格的基础上新增两个UI元素即可:- 选中背景视图:放在单元格内容视图最底层,左右撑满、上下留合适边距,用来实现连续选中的连片效果,注意相邻cell的选中背景不要留左右边距,不然会出现拼接缝隙
- 右上角序号标签:UILabel控件,放在选中背景视图上方、日期数字的右上角,字号比日期数字小2-3号,做圆角处理,默认隐藏,用自动布局钉在单元格右上角,留2-3pt的边距即可,文字色和选中背景做足够的对比度。
集合视图渲染逻辑
在collectionView(_:cellForItemAt:)数据源方法里,拿到当前cell对应的日期后做匹配渲染:- 先把当前cell的日期做同样的归一化处理
- 查
date2IndexMap中是否存在当前日期的key:- 不存在:走普通未选中逻辑,隐藏选中背景、隐藏序号标签,设置普通日期文字色
- 存在:取出对应序号,显示选中背景、显示序号标签并赋值对应数字,同时根据当前日期在排序后选中数组里的位置,给选中背景设置不同的圆角,实现连续选中两端圆角、中间无圆角的效果:
- 选中时段只有1天:四个角全加圆角
- 是选中时段的第一天:只加左上下两个角的圆角
- 是选中时段的最后一天:只加右上下两个角的圆角
- 是选中时段的中间日期:不加圆角,和相邻cell的选中背景自然拼接
参考代码:
let currentCellDate = stripTime(from: cellModel.date) if let orderNum = date2IndexMap[currentCellDate] { cell.showSelectedState = true cell.orderLabel.isHidden = false cell.orderLabel.text = "\(orderNum)" // 处理圆角逻辑 let isFirstDay = orderNum == 1 let isLastDay = orderNum == sortedSelectedDates.count if sortedSelectedDates.count == 1 { cell.selectedBgView.layer.cornerRadius = 8 cell.selectedBgView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner] } else if isFirstDay { cell.selectedBgView.layer.cornerRadius = 8 cell.selectedBgView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner] } else if isLastDay { cell.selectedBgView.layer.cornerRadius = 8 cell.selectedBgView.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMaxXMaxYCorner] } else { cell.selectedBgView.layer.cornerRadius = 0 } } else { cell.showSelectedState = false cell.orderLabel.isHidden = true cell.selectedBgView.layer.cornerRadius = 0 }
踩坑提示:如果你的日历支持跨月展示预选中时段,只需要在每次切换月份触发collectionView刷新前,确保当前月的所有日期都做了归一化匹配即可,不需要额外写跨月判断逻辑。
内容的提问来源于stack exchange,提问作者Abhishek Kumar
相关产品推荐
相关产品推荐

