iOS仿酒店入住离店日历(Calendar)选择

最近做一个项目,有个日历选择功能,就仿美团做了一个,将思路、代码分享一下。先上图片,直观体验一下
Demo下载地址:
https://github.com/TechAlleyBoy/CalendarDemo
http://download.csdn.net/download/techalleyboy/9840841
iOS仿酒店入住离店日历(Calendar)选择_第1张图片
iOS仿酒店入住离店日历(Calendar)选择_第2张图片
一:数据源的准备工作,各种日期的计算工作

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。如下图:
iOS仿酒店入住离店日历(Calendar)选择_第3张图片

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;
}

你可能感兴趣的:(iOS)