前言

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

效果

功能

  • 支持单选、区间
  • 支持默认选中日期
  • 支持限制月份
  • 支持过去、当前、未来模式
  • 支持frameAutoLayout

原理

层次结构使用UIStackView布局,UICollectionView复用,背景使用DecorationView

核心代码

日历核心代码在于计算每个月的日期,主要代码如下:

func month() -> [CLCalendarMonthModel] {
func day(with date: Date, section: Int) -> [CLCalendarDayModel] {
var newDate = date
let tatalDay = newDate.daysInMonth
let 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.days
if 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 = 0
if config.type == .past {
value = config.limitMonth - 1
} else if config.type == .today {
value = config.limitMonth / 2
}
return value
}() let start = todayDate - month.months
for i in 0 ..< config.limitMonth {
let date = start + i.months
let 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 past
case today
case future
} enum CLSelectType {
case single
case 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: Int64
init(rawValue: Int64) {
self.rawValue = rawValue
}
} struct CLColor {
var background = "#ffffff".uiColor
var topToolBackground = "#F4F4F4".uiColor
var topToolText = "#444444".uiColor
var topToolTextWeekend = "#3CCA79".uiColor
var sectionBackgroundText = "f2f2f2".uiColor
var selectStartBackground = "#4bce817f".uiColor
var selectBackground = "#afe9c77f".uiColor
var selectEndBackground = "#4bce817f".uiColor
var todayText = "#32cd32".uiColor
var titleText = "#555555".uiColor
var subtitleText = "#555555".uiColor
var selectTodayText = "#32cd32".uiColor
var selectTitleText = "#ffffff".uiColor
var selectSubtitleText = "#ffffff".uiColor
var failureTitleText = "#a9a9a9".uiColor
var failureSubtitleText = "#a9a9a9".uiColor
var failureBackground = "#dcdcdc32".uiColor
} var color = CLColor()
var selectBegin: Date?
var selectEnd: Date?
var limitMonth = 12
var type = CLCalendarType.today
var selectType = CLSelectType.area
var touchType: CLTouchType = [.today, .past]
var isShowLunarCalendar = true
var insetsLayoutMarginsFromSafeArea = true
var headerHight = 50.0
}

总结

定制化开发请自行参考CLDemo修改 , 如果喜欢,欢迎star。

参考资料

  1. DateToolsSwift

  2. UICollectionView: 装饰视图 Decoration View

  3. 使用UIStackView来简化iOS的界面布局

iOS开发之自定义日历控件的更多相关文章

  1. iOS开发UI篇—UITableview控件基本使用

    iOS开发UI篇—UITableview控件基本使用 一.一个简单的英雄展示程序 NJHero.h文件代码(字典转模型) #import <Foundation/Foundation.h> ...

  2. iOS开发UI篇—UITableview控件使用小结

    iOS开发UI篇—UITableview控件使用小结 一.UITableview的使用步骤 UITableview的使用就只有简单的三个步骤: 1.告诉一共有多少组数据 方法:- (NSInteger ...

  3. javascript实例学习之六—自定义日历控件

    基于之前上篇博客轻量级jquery,tool.js和base.js.自定义开发的base_datePicker插件,效果类似于jquery_ui的datePicker插件 //基于Base.js以及t ...

  4. iOS开发UI篇—UIScrollView控件实现图片缩放功能

    iOS开发UI篇—UIScrollView控件实现图片缩放功能 一.缩放 1.简单说明: 有些时候,我们可能要对某些内容进行手势缩放,如下图所示 UIScrollView不仅能滚动显示大量内容,还能对 ...

  5. iOS开发UI篇—UIScrollView控件介绍

    iOS开发UI篇—UIScrollView控件介绍 一.知识点简单介绍 1.UIScrollView控件是什么? (1)移动设备的屏幕⼤大⼩小是极其有限的,因此直接展⽰示在⽤用户眼前的内容也相当有限 ...

  6. iOS开发UI篇—UITableview控件简单介绍

    iOS开发UI篇—UITableview控件简单介绍 一.基本介绍 在众多移动应⽤用中,能看到各式各样的表格数据 . 在iOS中,要实现表格数据展示,最常用的做法就是使用UITableView,UIT ...

  7. iOS开发UI篇—UIScrollView控件实现图片轮播

    iOS开发UI篇—UIScrollView控件实现图片轮播 一.实现效果 实现图片的自动轮播            二.实现代码 storyboard中布局 代码: #import "YYV ...

  8. 【转】 iOS开发UI篇—UIScrollView控件实现图片轮播

    原文:http://www.cnblogs.com/wendingding/p/3763527.html iOS开发UI篇—UIScrollView控件实现图片轮播 一.实现效果 实现图片的自动轮播 ...

  9. ios开发中关闭textview控件的虚拟键盘

    在ios开发中,textfield控件在点击的时候出现虚拟键盘,关掉虚拟键盘可以通过虚拟键盘中的done button和点击view中的任意地方来关闭虚拟键盘. 1.第一种方法是textfield控件 ...

随机推荐

  1. Luogu1099 树网的核 (暴力?,floyd?)(还未想正解,暴力就A了)

    阅读理解两小时,手敲暴力思考5分钟.然后\(n^3\)就A了 暴力代码 #include <iostream> #include <cstdio> #include <c ...

  2. Spring源码 17 IOC refresh方法12

    参考源 https://www.bilibili.com/video/BV1tR4y1F75R?spm_id_from=333.337.search-card.all.click https://ww ...

  3. cmake错误的解决

    安装Mysql时出现:CMake Error: Error executing cmake:: LoadCache(). Aborting. 对比:/usr/bin/cmake 和 /usr/loca ...

  4. 推荐软件(一):Motrix——磁力下载器

    个人觉得迅雷这样的下载器广告又多,启动速度又慢,又占用内存和存储,非常地不好用.有时候下载速度也不是你自己网速的最大值,而且有一些资源也会因为版权问题阻止你下载. Motrix 界面非常简洁:下载速度 ...

  5. 零基础学Java(14)对象构造

    对象构造 之前学习了编写简单的构造器,可以定义对象的初始状态.但是,由于对象构造非常重要,所以Java提供了多种编写构造器的机制. 重载 有些类有多个构造器.例如,可以如下构造一个空的StringBu ...

  6. CF1450G. Communism(状压DP)

    题面 有一个字符串 s \tt s s 和一个有理数 k \tt k k,可以进行如下操作任意次: 选一个当前串中存在的字符 x \tt x x ,令 i 1 , i 2 , . . . , i m ...

  7. [CF1481D] AB Graph(构造)

    题解 给一个 n \tt n n 个点的完全有向图, ( u , v ) \tt(u,v) (u,v) 或者 ( v , u ) \tt(v,u) (v,u) 都有一条边,前提是 u ≠ v \tt ...

  8. KingbaseES 的 Lateral 连接

    一.什么是 Lateral 连接 根据文档,它的作用是: LATERAL 关键字可以位于子 SELECT FROM 项之前.这允许子 SELECT 引用 FROM 列表中出现在它之前的 FROM 项的 ...

  9. Sentinel 源码分析- 熔断降级原理分析

    直接从Sentinel 源码demo ExceptionRatioCircuitBreakerDemo看起 直接看他的main函数 public static void main(String[] a ...

  10. Hybrid app本地开发如何调用JSBridge

    前天同事问我公司内部的小程序怎么对接的,我回忆了一下,简单记录了一下前端同学需要注意的点. 背后还有小程序架构.网络策略等等.当时恰逢小程序架构调整,(老架构的时候我就发现了有一个问题点可以优化,但是 ...