swift自定义日历(暂不支持滑动切换)
拒绝转载,抄袭更不允许!!!!求别污染分享环境
原图是这样的
功能介绍
- 暂不支持滑动切换月份,navigator右边是切换月份,每切换一次的月份会网络请求新的数据。
- 当月的下一个月不能点击
- 星期六日样式不一样,点击到当前cell的样式不一样,还没到的日期样式不一样,且不能点击(这优先级高)
- 当月的话是默认点击当天的位置,若非本月默认第一个(可做有数据的第一个)
- 数据返回的不是以每一天来返回,要对数据的时间进行遍历分析
- 若点击到没有数据的cell,底部的信息框显示一样的信息
日历原理
其实就是一个collectionview
,第一天在星期几为了前面空多少个,有多少天就是确定collectionview的item有多少个(第一天的index + 天数)。collectionviewDataSource
负责处理返回的数据,这就要把返回的数据的时间转换成day,然后判断day是否等于显示的天相等。再把数据放入。collectionviewdelegate
负责点击事件判断,已点、未点、不能点
时间逻辑核心代码(工具类)
有获取当前月、上下月的第一天是星期几,当前月有多少天,date转string,string转date
class DateTools: NSObject {
/// Date转换String
///
/// - Parameters:
/// - date: 日期
/// - format: 格式
/// - Returns: 字符串日期
class func stringFromDate(date: Date, format: String = "yyyy-MM-dd HH:mm:ss") -> String {
let formatter = DateFormatter.init()
formatter.locale = Locale(identifier: "zh_CN")
formatter.dateFormat = format
let dateString = formatter.string(from: date)
return dateString
}
/// 上个月
///
/// - Parameter date: 当前日期
/// - Returns: 上月日期
class func lastMonth(_ date: Date) -> Date {
var dateCom = DateComponents()
dateCom.month = -1
let newDate = (Calendar.current as NSCalendar).date(byAdding: dateCom, to: date, options: NSCalendar.Options.matchStrictly)
return newDate!
}
/// 下个月
///
/// - Parameter date: 当前日期
/// - Returns: 下个月日期
class func nextMonth(_ date: Date) -> Date {
var dateCom = DateComponents()
let abc = 1
dateCom.month = +abc
let newDate = (Calendar.current as NSCalendar).date(byAdding: dateCom, to: date, options: NSCalendar.Options.matchStrictly)
return newDate!
}
/// 当月的天数
///
/// - Parameter date: 日期
/// - Returns: 天数
class func daysInCurrMonth(date: Date) -> Int {
let days: NSRange = (Calendar.current as NSCalendar).range(of: NSCalendar.Unit.day, in: NSCalendar.Unit.month, for: date)
return days.length
}
/// 当前月份的第一天是周几
///
/// - Parameter date: 当前日期
/// - Returns: 周几
class func firstDayIsWeekInMonth(date: Date) -> Int {
var calender = Calendar.current
calender.firstWeekday = 1
var com = (calender as NSCalendar).components([NSCalendar.Unit.year, NSCalendar.Unit.month, NSCalendar.Unit.day], from: date)
com.day = 1
let firstDay = calender.date(from: com)
let firstWeek = (calender as NSCalendar).ordinality(of: NSCalendar.Unit.weekday, in: NSCalendar.Unit.weekOfMonth, for: firstDay!)
return firstWeek - 1
}
/// 当前月份的几号
///
/// - Parameter date: 当前月份
/// - Returns: 几号
class func day(_ date: Date) -> Int {
let com = (Calendar.current as NSCalendar).components([NSCalendar.Unit.year, NSCalendar.Unit.month, NSCalendar.Unit.day], from: date)
return com.day!
}
实现逻辑
collectrionview的创建
// 数据使用要跟日匹配
var count = 0
var lastDay: String = ""
var toDetailDate: String = "" //传出的日期
let margin: CGFloat = 10.0
let paddingLeft: CGFloat = 20.0
var fullMoonRecordModel: FullMoonRecordModel?
let itemWidth: CGFloat = (SCREEN_WIDTH - 100)/7.0
fileprivate var OFFSET:CGFloat = AppDelegate.phoneType.VG_IS_IPhoneX_All ? 44:25
fileprivate var identifier: String = "daysCell"
fileprivate var date = Date()
fileprivate var isCurrentMonth: Bool = true //是否当月
fileprivate var currentMonthTotalDays: Int = 0 //当月的总天数
var firstDayIsWeekInMonth: Int = 0 //每月的一号对于的周几
fileprivate var lastSelectedItemIndex: IndexPath? //获取最后一次选中的索引
let today: String = String(DateTools.day(Date())) //当天几号
public lazy var calendarCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
//layout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0)
layout.itemSize = CGSize(width: itemWidth, height: itemWidth*1.325)
layout.minimumLineSpacing = margin
layout.minimumInteritemSpacing = margin
// let vWidth: CGFloat = SCREEN_WIDTH - paddingLeft * 2
// let tempRect = CGRect(x: paddingLeft, y: self.calendarHeadView.frame.maxY + 20 + OFFSET, width: vWidth, height: 300)
let calendarCollectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
// calendarCollectionView.frame = CGRect.zero
// calendarCollectionView.collectionViewLayout = layout
calendarCollectionView.backgroundColor = UIColor.makeColorBy_RGB(r: 242, g: 242, b: 242, a: 1.0)
calendarCollectionView.dataSource = self
calendarCollectionView.delegate = self
calendarCollectionView.register(DaysCell.self, forCellWithReuseIdentifier: self.identifier)
return calendarCollectionView
}()
collectionview的实现(代码会删掉一些,敬请原谅,如有疑问可以直接私聊我)
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.identifier, for: indexPath) as! DaysCell
cell.clearDaysLabelStyle()
var day = 0
let index = indexPath.row
cell.classHour.text = ""
cell.timeFace.text = ""
if index < self.firstDayIsWeekInMonth {
cell.daysLabel.text = ""
cell.backgroundColor = UIConstants.color_002
// cell.daysLabel.backgroundColor = UIConstants.color_002
} else {
day = index - self.firstDayIsWeekInMonth + 1
cell.daysLabel.text = String(day)
if model?.data?.count ?? 0 > count{
if model?.data?[count].date != nil{
let hasday = Date.getDay(date: model?.data?[count].date ?? "")
if lastDay == hasday{
count += 1
day -= 1
}
if String(day) == hasday{
lastDay = hasday
cell.model = model?.data?[count]
if model?.data?[count].expendcnt != 0{
//整数判断
var remainClass: String = ""
if model?.data?[count].expendcnt ?? 0.0 == floor(model?.data?[count].expendcnt ?? 0){
}else{
}
}else{
}
count += 1
}
}
}
cell.backgroundColor = UIColor.white
cell.isSelectedItem = false
cell.daysLabel.textColor = UIColor.init(hex: "333333")
cell.timeFace.textColor = UIColor.init(hex: "666666")
cell.classHour.textColor = UIColor.init(hex: "1797CE")
// if originPointArray.contains(day) {
// cell.isPointHidden = true
// }
cell.isWeekend = false
cell.isDisable = false
if isCurrentMonth {
//当天
if cell.daysLabel.text == today {
let cellModel = cell.model
cell.clickBack = { [weak self] in
self?.clickCell(cellModel: cellModel, today: self?.today ?? "")
}
cell.isSelectedItem = true
cell.backgroundColor = UIColor.init(hex: "1797CE")
// cell.backgroundColor =
// cell.isSelectedItem = false
cell.daysLabel.textColor = UIColor.white
cell.timeFace.textColor = UIColor.white
cell.classHour.textColor = .white
// collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .centeredVertically)
self.lastSelectedItemIndex = indexPath
}
//当月当天以前的日期置灰,不可点击
let itemValue = cell.daysLabel.text!
let currDay = Int(itemValue)
if currDay! > Int(today)! {
cell.isDisable = true
cell.backgroundColor = UIConstants.color_002
// cell.daysLabel.backgroundColor = UIConstants.color_002
}else{
if index%7 == 0 || index%7 == 6{
cell.isDisable = true
cell.isWeekend = true
}else{
cell.isWeekend = false
cell.isDisable = false
}
}
}
else {
//非当前自然月的1号默认选中
if cell.daysLabel.text == "1" {
var fcellModel:dayRecordData? = dayRecordData()
if model?.data?.count ?? 0 > count{
fcellModel = model?.data?[count]
}
cell.clickBack = { [weak self] in
self?.clickCell(cellModel: fcellModel, today: "1")
}
cell.isSelectedItem = true
self.lastSelectedItemIndex = indexPath
// collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .centeredVertically)
// cell.isSelectedItem = false
} else {
cell.isSelectedItem = false
}
if index%7 == 0 || index%7 == 6{
cell.isDisable = true
cell.isWeekend = true
}else{
cell.isWeekend = false
cell.isDisable = false
}
}
}
return cell
}
点击事件处理
//UICollectionViewDelegate
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let currentCell = collectionView.cellForItem(at: indexPath) as! DaysCell
//是否已经选中
guard !currentCell.isSelectedItem else {
return
}
//是否有值
let itemText: String = currentCell.daysLabel.text!
guard !itemText.isEmpty else {
return
}
let curDate = Date()
let today = DateTools.day(curDate)
let currDay = Int(itemText)
//选中日期小于当天并且非当月
if self.isCurrentMonth && currDay! > today {
return
}
//获取上一次选中的item
let preCell = collectionView.cellForItem(at: self.lastSelectedItemIndex!) as! DaysCell
preCell.isSelectedItem = false
//获取当前选中的item
currentCell.isSelectedItem = true
self.lastSelectedItemIndex = indexPath
if currentCell.classHour.text != "" || currentCell.timeFace.text != ""{
bottomView.isHidden = false
bottomView.isUserInteractionEnabled = true
toImageView.isHidden = false
let Monthday = currentCell.model?.date ?? ""
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let d = dateFormatter.date(from: Monthday)
toDetailDate = Monthday
bottomDay.text = String("\(d?.changeToString(formatterString: "M月d日") ?? "")·")
var remainClass: String = ""
if currentCell.model?.expendcnt ?? 0.0 == floor(currentCell.model?.expendcnt ?? 0){
remainClass = String(Int(currentCell.model?.expendcnt ?? 0))
}else{
remainClass = String(currentCell.model?.expendcnt ?? 0.0)
}
if currentCell.model?.faceCnt != 0 && currentCell.model?.expendcnt != 0{
bottomComment.text = "刷脸\(currentCell.model?.faceCnt ?? 0)次/扣减\(remainClass)课时"
}else if currentCell.model?.faceCnt == 0 && currentCell.model?.expendcnt == 0{
bottomComment.text = ""
}else if currentCell.model?.expendcnt == 0{
bottomComment.text = "刷脸\(currentCell.model?.faceCnt ?? 0)次"
}else{
bottomComment.text = "扣减\(remainClass)课时"
}
timeLabel.text = currentCell.model?.lastFaceInfo?.punchtime ?? ""
if timeLabel.text == ""{
bottomView.isUserInteractionEnabled = false
toImageView.isHidden = true
timeLabel.text = "当日暂无刷脸记录"
}
addressLabel.text = currentCell.model?.lastFaceInfo?.address ?? ""
}else{
bottomView.isHidden = false
let mon = DateTools.stringFromDate(date: date, format: "M")
let day:String = currentCell.daysLabel.text ?? ""
bottomDay.text = "\(mon)月\(day)日"
timeLabel.text = "当日暂无刷脸记录"
bottomComment.text = ""
bottomView.isUserInteractionEnabled = false
addressLabel.text = ""
toImageView.isHidden = true
}
}
clickCell函数不会给出来,但原理就是底部的信息框的处理
func clickCell(cellModel:, today: String){
切换月历处理事件(完整代码)
@objc func lastAction() {
self.date = DateTools.lastMonth(date)
let monDay = DateTools.stringFromDate(date: date, format: "yyyy-MM")
self._initCalendarInfo()
count = 0
lastDay = ""
lastMon?(monDay)
bottomView.isHidden = true
if isCurrentMonth {
nextMonthButton.isEnabled = false
}else{
nextMonthButton.isEnabled = true
}
}
@objc func nextAction() {
self.date = DateTools.nextMonth(date)
print("date")
print(date)
count = 0
let monDay = DateTools.stringFromDate(date: date, format: "yyyy-MM")
self._initCalendarInfo()
bottomView.isHidden = true
lastDay = ""
nextMon?(monDay)
if isCurrentMonth {
nextMonthButton.isEnabled = false
}else{
nextMonthButton.isEnabled = true
}
}
vc就负责对事件的处理、传递数据、网络请求而已,所以没必要添上去了