最近做一个项目,有个日历选择功能,就仿美团做了一个,将思路、代码分享一下。先上图片,直观体验一下
Demo下载地址:
https://github.com/TechAlleyBoy/CalendarDemo
http://download.csdn.net/download/techalleyboy/9840841
一:数据源的准备工作,各种日期的计算工作
1:根据NSDate获得年月日,及星期几
#pragma mark - 获取年,月,日,星期
//注:日历获取在9.x之后的系统使用currentCalendar会出异常。在8.0之后使用系统新API。
-(NSInteger )getDataFromDate:(NSDate *)date type:(NSString * )type{
NSCalendar *calendar = nil;
if ([NSCalendar respondsToSelector:@selector(calendarWithIdentifier:)]) {
calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
}else{
calendar = [NSCalendar currentCalendar];
}
NSDateComponents *components = [calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay |NSCalendarUnitWeekday) fromDate:date];
if ([type isEqualToString:@"year"]) {
return components.year;
}else if ([type isEqualToString:@"month"]) {
return components.month;
}else if ([type isEqualToString:@"day"]) {
return components.day;
}else if ([type isEqualToString:@"week"]) {
return components.weekday;
}else{
return 0;
}
}
2:根据NSDate获得当月一共几天
#pragma mark -- 获取当前月共有多少天
- (NSInteger)totaldaysInMonth:(NSDate *)date{
NSRange daysInLastMonth = [[NSCalendar currentCalendar] rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:date];
return daysInLastMonth.length;
}
3:时间字符串转时间
#pragma mark - 时间字符串转时间
-(NSDate *)dateWithYear:(NSInteger )year month:(NSInteger )month day:(NSInteger )day
{
NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
[formatter setDateFormat:@"yyyyMMdd"];
return [formatter dateFromString:[NSString stringWithFormat:@"%ld%02ld%02ld",year,month,day]];
}
二:模型Model的建立
注:模型里面套模型 月的模型(MonthModel) 中 有一个NSArray
//
// MonthModel.h
// BJTResearch
//
// Created by yunlong on 17/5/12.
// Copyright © 2017年 yunlong. All rights reserved.
//
#import
#import
typedef enum : NSUInteger {
DayModelStateNormal = 0,
DayModelStateStart,
DayModelStateEnd,
DayModelStateSelected,
} DayModelState;
typedef enum : NSUInteger {
Sunday = 1,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
} DayModelOfTheWeek;
@interface DayModel : NSObject
/**
* 年
*/
@property(nonatomic,assign)NSInteger year;
/**
* 月
*/
@property(nonatomic,assign)NSInteger month;
/**
* 日
*/
@property(nonatomic,assign)NSInteger day;
/**
* 日期
*/
@property(nonatomic,strong)NSDate *dayDate;
/**
* 星期
*/
@property(nonatomic,assign)DayModelOfTheWeek dayOfTheWeek;
/**
* 日期的状态
*/
@property(nonatomic,assign)DayModelState state;
/**
* 日期是不是今天
*/
@property(nonatomic,assign)BOOL isToday;
@end
@interface MonthModel : NSObject
/**
* 年
*/
@property(nonatomic,assign)NSInteger year;
/**
* 月
*/
@property(nonatomic,assign)NSInteger month;
/**
* 一个月中UICollectionViewCell的个数
*/
@property(nonatomic,assign)NSInteger cellNum;
/**
* 月UITableViewCell的高度
*/
@property(nonatomic,assign)CGFloat cellHight;
/**
* UICollectionViewCell开始的位置
*/
@property(nonatomic,assign)NSInteger cellStartNum;
/**
* 月DayModel数组
*/
@property(nonatomic,strong)NSArray * days;
@end
2:.m文件
#import "MonthModel.h"
@implementation DayModel
@end
@implementation MonthModel
@end
三:数据源建立
几点说明
1:采用数据源懒加载,用到时加载。
2:这个demo一共加载了13个月:for (int i = 0; i<13; i++)
3:i == 0特殊处理额原因,因为第一个月的起始日期是当前日期,不是1号。
4:NSInteger oneLineCoune =( 7 - m.dayOfTheWeek + 2 ) % 7;这是计算第一行中cell的个数,components.weekday中,weekday=1是周日,周一是2。
/////////////////////////////////////数据处理///////////////////////////////////////////
#pragma mark - 懒加载数据源
-(NSMutableArray *)dataArray{
if (!_dataArray) {
_dataArray = [NSMutableArray array];
NSDate *nowdate = [NSDate date];
NSInteger toYear = [self getDataFromDate:nowdate type:@"year"];
NSInteger toMonth = [self getDataFromDate:nowdate type:@"month"];
for (int i = 0; i<13; i++) {
if (i == 0) {
MonthModel * monthModel = [[MonthModel alloc] init];
monthModel.year = toYear;
monthModel.month = toMonth;
NSMutableArray *days = [NSMutableArray array ];
NSInteger starNum = [self getDataFromDate :nowdate type:@"day"];
for (NSInteger i = starNum ; i <=[self totaldaysInMonth:nowdate]; i++) {
DayModel *dayModel = [[DayModel alloc]init];
dayModel.dayDate = [self dateWithYear:monthModel.year month:monthModel.month day:i];
dayModel.day = i;
dayModel.month = monthModel.month;
dayModel.year = monthModel.year;
dayModel.dayOfTheWeek = [self getDataFromDate:dayModel.dayDate type:@"week"];
dayModel.isToday = i==starNum;
dayModel.state = DayModelStateNormal;
[days addObject:dayModel];
}
monthModel.days = days;
DayModel *m = days.firstObject;
NSInteger lineCount = 1;
NSInteger oneLineCoune =( 7 - m.dayOfTheWeek + 2 ) % 7;
if (oneLineCoune == 0) {
oneLineCoune = 7;
}
NSInteger count = days.count - oneLineCoune;
if (count%7==0) {
lineCount = lineCount + count/7 ;
}else{
lineCount = lineCount + count/7 + 1 ;
}
monthModel.cellNum = lineCount * 7;
monthModel.cellStartNum = 7 - oneLineCoune ;
monthModel.cellHight = 60 + 60 * lineCount + 2 * (lineCount + 1);
[_dataArray addObject:monthModel];
toMonth++;
}else{
if (toMonth == 13) {
toMonth = 1;
toYear += 1;
}
NSDate *toDate = [self dateWithYear:toYear month:toMonth day:1];
MonthModel * monthModel = [[MonthModel alloc] init];
monthModel.year = [self getDataFromDate:toDate type:@"year"];
monthModel.month = [self getDataFromDate:toDate type:@"month"];
NSMutableArray *days = [NSMutableArray array ];
for (NSInteger i = 1 ; i <=[self totaldaysInMonth:toDate]; i++) {
DayModel *dayModel = [[DayModel alloc]init];
dayModel.dayDate = [self dateWithYear:monthModel.year month:monthModel.month day:i];
dayModel.day = i;
dayModel.month = monthModel.month;
dayModel.year = monthModel.year;
dayModel.dayOfTheWeek = [self getDataFromDate:dayModel.dayDate type:@"week"];
dayModel.isToday = NO;
dayModel.state = DayModelStateNormal;
[days addObject:dayModel];
}
monthModel.days = days;
DayModel *m = days.firstObject;
NSInteger lineCount = 1;
NSInteger oneLineCoune =( 7 - m.dayOfTheWeek + 2 ) % 7;
if (oneLineCoune == 0) {
oneLineCoune = 7;
}
NSInteger count = days.count - oneLineCoune;
if (count%7==0) {
lineCount = lineCount + count/7 ;
}else{
lineCount = lineCount + count/7 + 1 ;
}
monthModel.cellNum = lineCount * 7;
monthModel.cellStartNum = 7 - oneLineCoune ;
monthModel.cellHight = 60 + 60 * lineCount + 2 * (lineCount + 1);
[_dataArray addObject:monthModel];
toMonth++;
}
}
}
return _dataArray;
}
四:视图创建
1:星期视图是个单独的view,详见代码
2:整体是tableview中嵌套collectionview,每个月是一个tableviewcell,每天是一个collectionviewcell。如下图:
3:UICollectionViewCell里面还有节假日及公历的计算,详见代码
四:特殊注意的几个地方
1:选择时间分为几种情况
1):没有入住,没有离店
2):有入住,没离店
3):有入住,有离店
__weak typeof(self) weakSelf = self;
cell.selectedDay = ^(DayModel *returnDaymodel){
BOOL isHaveStart = NO;
BOOL isHaveEnd = NO;
BOOL isHaveSelected = NO;
NSDate *startDate ;
NSDate *endDate ;
DayModel *starModel;
DayModel *endModel;
for (MonthModel *Mo in self.dataArray) {
for (DayModel *mo in Mo.days) {
if (mo.state == DayModelStateStart) {
isHaveStart = YES;
startDate = mo.dayDate;
starModel = mo;
}else if (mo.state == DayModelStateSelected) {
isHaveSelected = YES;
}else if (mo.state == DayModelStateEnd) {
isHaveEnd = YES;
endDate = mo.dayDate;
endModel = mo;
break;
}
}
}
if ((!isHaveStart && !isHaveEnd && !isHaveSelected )|| (!isHaveStart && !isHaveEnd) ) {
//没有设置开始结束
returnDaymodel.state = DayModelStateStart;
}else if ((isHaveEnd && isHaveStart)){
//有开始有结束
for (MonthModel *Mo in weakSelf.dataArray) {
for (DayModel *mo in Mo.days) {
mo.state = DayModelStateNormal;
}
}
returnDaymodel.state = DayModelStateStart;
}else if(isHaveStart && !isHaveEnd){
//有开始没有结束
NSInteger ci = [self compareDate:returnDaymodel.dayDate withDate:startDate];
switch (ci) {
case 1://startDate > currentSelectDate
starModel.state = DayModelStateNormal;
returnDaymodel.state = DayModelStateStart;
break;
case -1:
returnDaymodel.state = DayModelStateEnd;
for (MonthModel *Mo in weakSelf.dataArray) {
for (DayModel *mo in Mo.days) {
NSInteger ci1 = [weakSelf compareDate:mo.dayDate withDate:startDate];
NSInteger ci2 = [weakSelf compareDate:mo.dayDate withDate:returnDaymodel.dayDate];
if (ci1 == -1 && ci2 == 1 ) {
mo.state = DayModelStateSelected;
}
}
}
break;
case 0:
returnDaymodel.state = DayModelStateNormal;
break;
default:
break;
}
}
[weakSelf.tableView reloadData];
};
2:选择入住和离店时间,及其总时长时,会用到下面两个方法
#pragma mark-日期比较
-(NSInteger )compareDate:(NSDate *)date01 withDate:(NSDate *)date02{
NSInteger ci;
NSComparisonResult result = [date01 compare:date02];
switch (result)
{
//date02比date01大
case NSOrderedAscending: ci=1; break;
//date02比date01小
case NSOrderedDescending: ci=-1; break;
//date02=date01
case NSOrderedSame: ci=0; break;
default: NSLog(@"erorr dates %@, %@", date02, date01); break;
}
return ci;
}
#pragma mark - 计算两个日期之间的天数
- (NSInteger) calcDaysFromBegin:(NSDate *)beginDate end:(NSDate *)endDate{
//创建日期格式化对象
NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm"];
//取两个日期对象的时间间隔:
//这里的NSTimeInterval 并不是对象,是基本型,其实是double类型,是由c定义的:typedef double NSTimeInterval;
NSTimeInterval time=[endDate timeIntervalSinceDate:beginDate];
int days=((int)time)/(3600*24);
//int hours=((int)time)%(3600*24)/3600;
//NSString *dateContent=[[NSString alloc] initWithFormat:@"%i天%i小时",days,hours];
return days;
}