iOS开发之自定义日历控件

前言日常开发中经常会遇到日期选择,为了方便使用,简单封装了一个日历控件,在此抛砖引玉供大家参考 。
效果

iOS开发之自定义日历控件

文章插图
功能
  • 支持单选、区间
  • 支持默认选中日期
  • 支持限制月份
  • 支持过去、当前、未来模式
  • 支持frameAutoLayout
原理层次结构使用UIStackView布局,UICollectionView复用,背景使用DecorationView
核心代码日历核心代码在于计算每个月的日期,主要代码如下:
func month() -> [CLCalendarMonthModel] {func day(with date: Date, section: Int) -> [CLCalendarDayModel] {var newDate = datelet tatalDay = newDate.daysInMonthlet firstDay = max(0, newDate.weekday - 1)let columns = Int(ceil(CGFloat(tatalDay + firstDay) / CGFloat(weekArray.count)))var resultArray = [CLCalendarDayModel]()for column in 0 ..< columns {for weekDay in 0 ..< weekArray.count {if column == 0,weekDay <= firstDay - 1{resultArray.append(CLCalendarDayModel())} else {let subtitle: String? = {guard !newDate.isToday else { return "今天" }guard config.isShowLunarCalendar else { return nil }guard let index = chinese.dateComponents([.day], from: newDate).day else { return nil }return chineseDayArray[index - 1]}()let type: CLCalendarDayModel.CLCalendarDayType = {guard !newDate.isToday else { return .today }guard newDate.compare(todayDate) == .orderedDescending else { return .future }return .past}()let dayModel = CLCalendarDayModel(title: "\(newDate.day)", date: newDate, subtitle: subtitle, type: type)resultArray.append(dayModel)newDate = newDate + 1.daysif beginDate?.year == dayModel.date?.year,beginDate?.month == dayModel.date?.month,beginDate?.day == dayModel.date?.day{startIndexPath = .init(row: 0, section: section)}guard (resultArray.count - firstDay) != tatalDay else { break }}}}return resultArray}var resultArray = [CLCalendarMonthModel]()let month: Int = {var value = https://www.huyubaike.com/biancheng/0if config.type == .past {value = config.limitMonth - 1} else if config.type == .today {value = config.limitMonth / 2}return value}()let start = todayDate - month.monthsfor i in 0 ..< config.limitMonth {let date = start + i.monthslet headerModel = CLCalendarMonthModel(headerText: date.format(with:"yyyy年MM月"),month: date.format(with: "MM"),daysArray: day(with: Date(year: date.year, month: date.month, day: 1), section: i))resultArray.append(headerModel)}return resultArray}基础配置struct CLCalendarConfig {enum CLCalendarType {case pastcase todaycase future}enum CLSelectType {case singlecase area}struct CLTouchType: OptionSet {static let past = CLTouchType(rawValue: 1)static let today = CLTouchType(rawValue: 1 << 1)static let future = CLTouchType(rawValue: 1 << 2)let rawValue: Int64init(rawValue: Int64) {self.rawValue = https://www.huyubaike.com/biancheng/rawValue}}struct CLColor {var background ="#ffffff".uiColorvar topToolBackground = "#F4F4F4".uiColorvar topToolText = "#444444".uiColorvar topToolTextWeekend = "#3CCA79".uiColorvar sectionBackgroundText = "f2f2f2".uiColorvar selectStartBackground = "#4bce817f".uiColorvar selectBackground = "#afe9c77f".uiColorvar selectEndBackground = "#4bce817f".uiColorvar todayText = "#32cd32".uiColorvar titleText = "#555555".uiColorvar subtitleText = "#555555".uiColorvar selectTodayText = "#32cd32".uiColorvar selectTitleText = "#ffffff".uiColorvar selectSubtitleText = "#ffffff".uiColorvar failureTitleText = "#a9a9a9".uiColorvar failureSubtitleText = "#a9a9a9".uiColorvar failureBackground = "#dcdcdc32".uiColor}var color = CLColor()var selectBegin: Date?var selectEnd: Date?var limitMonth = 12var type = CLCalendarType.todayvar selectType = CLSelectType.areavar touchType: CLTouchType = [.today, .past]var isShowLunarCalendar = truevar insetsLayoutMarginsFromSafeArea = truevar headerHight = 50.0}总结定制化开发请自行参考CLDemo修改,如果喜欢,欢迎star 。
参考资料
  1. DateToolsSwift
  2. UICollectionView:装饰视图 Decoration View
  3. 使用UIStackView来简化iOS的界面布局
【iOS开发之自定义日历控件】

    推荐阅读