iOS日历界面

作者: oldSix_Zhu | 来源:发表于2018-12-10 11:54 被阅读134次

    最近开发的产品中,有很多日历相关的需求,所以整理一下开发日历相关的东西。
    1、时间戳转换
    2、UI实现
    3、第三方库推荐
    4、值得注意的坑

    1、时间戳转换

    在开发日历相关功能的时候,后台与前端肯定是通过时间戳来通信,所以当我们遇到了有关日历的需求的时候,首先要准备一个工具类,把时间戳和时间信息来回转化。同时要注意:java的时间戳一般是毫秒级的,OC处理的时间戳是秒级的,所以后台传过来的时间戳要注意除1000。

    调接口的时候,可以使用时间戳转换网站来确认两端参数或数值的准确。
    下面是我使用的NSDate分类,基本可以满足所有的需求了。

    NSDate+Extension.h:

    #import <Foundation/Foundation.h>
    
    
    @interface NSDate (Extensions)
    
    /// 获取年
    + (NSInteger)year:(NSString *)dateStr;
    /// 获取月
    + (NSInteger)month:(NSString *)dateStr;
    /// 获取星期
    + (NSInteger)week:(NSString *)dateStr;
    /// 获取星期 中文 日
    + (NSString *)getWeekFromDate:(NSDate *)date;
    /// 获取星期 中文 周日
    + (NSString *)getChineseWeekFrom:(NSString *)dateStr;
    /// 获取日
    + (NSInteger)day:(NSString *)dateStr;
    /// 获取月共有多少天
    + (NSInteger)daysInMonth:(NSString *)dateStr;
    
    /// 获取当前日期 2018-01-01
    + (NSString *)currentDay;
    /// 获取当前小时 00:00
    + (NSString *)currentHour;
    /// 获取下月最后一天
    + (NSString *)nextMonthLastDay;
    
    /// 判断是否是今天
    + (BOOL)isToday:(NSString *)dateStr;
    /// 判断是否是明天
    + (BOOL)isTomorrow:(NSString *)dateStr;
    /// 判断是否是后天
    + (BOOL)isAfterTomorrow:(NSString *)dateStr;
    /// 判断是否是过去的时间
    + (BOOL)isHistoryTime:(NSString *)dateStr;
    
    /// 从时间戳获取具体时间 格式:6:00
    + (NSString *)hourStringWithInterval:(NSTimeInterval)timeInterval;
    /// 从时间戳获取具体小时 格式:6
    + (NSString *)hourTagWithInterval:(NSTimeInterval)timeInterval;
    /// 从毫秒级时间戳获取具体小时 格式:600
    + (NSString *)hourNumberWithInterval:(NSTimeInterval)timeInterval;
    /// 从时间戳获取具体日期 格式:2018-03-05
    + (NSString *)timeStringWithInterval:(NSTimeInterval)timeInterval;
    /// 从具体日期获取时间戳 毫秒
    + (NSTimeInterval)timeIntervalFromDateString:(NSString *)dateStr;
    
    /// 获取当前天的后几天的星期
    + (NSString *)getWeekAfterDay:(NSInteger)day;
    /// 获取当前天的后几天的日
    + (NSString *)getDayAfterDay:(NSInteger)day;
    /// 获取当前月的后几月
    + (NSString *)getMonthAfterMonth:(NSInteger)Month;
    
    @end
    
    
    /*
     G: 公元时代,例如AD公元
     yy: 年的后2位
     yyyy: 完整年
     MM: 月,显示为1-12
     MMM: 月,显示为英文月份简写,如 Jan
     MMMM: 月,显示为英文月份全称,如 Janualy
     dd: 日,2位数表示,如02
     d: 日,1-2位显示,如 2
     EEE: 简写星期几,如Sun
     EEEE: 全写星期几,如Sunday
     aa: 上下午,AM/PM
     H: 时,24小时制,0-23
     K:时,12小时制,0-11
     m: 分,1-2位
     mm: 分,2位
     s: 秒,1-2位
     ss: 秒,2位
     S: 毫秒
     Z:GMT  
     */
    
    

    NSDate+Extension.m:

    #import "NSDate+Extensions.h"
    
    @implementation NSDate (Extensions)
    
    /// 获取年
    + (NSInteger)year:(NSString *)dateStr {
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setDateFormat:@"yyyy-MM-dd"];
        [dateFormatter setLocale:[NSLocale currentLocale]];
        NSDate *startDate = [dateFormatter dateFromString:dateStr];
        NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:startDate];
        return components.year;
    }
    
    /// 获取月
    + (NSInteger)month:(NSString *)dateStr {
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setDateFormat:@"yyyy-MM-dd"];
        [dateFormatter setLocale:[NSLocale currentLocale]];
        NSDate *startDate = [dateFormatter dateFromString:dateStr];
        NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:startDate];
        return components.month;
    }
    
    
    /// 获取星期
    + (NSInteger)week:(NSString *)dateStr {
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setDateFormat:@"yyyy-MM-dd"];
        [dateFormatter setLocale:[NSLocale currentLocale]];
        NSDate *startDate = [dateFormatter dateFromString:dateStr];
        NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday) fromDate:startDate];
        return components.weekday - 1;
    }
    
    /// 获取星期 中文
    + (NSString *)getWeekFromDate:(NSDate *)date {
        NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday) fromDate:date];
        NSInteger week = components.weekday - 1;
        NSDictionary *weekDic = @{@"0":@"日",@"1":@"一",@"2":@"二",@"3":@"三",@"4":@"四",@"5":@"五",@"6":@"六"};
        NSString *key = [NSString stringWithFormat:@"%ld",(long)week];
        return weekDic[key];
    }
    
    /// 获取星期中文
    + (NSString *)getChineseWeekFrom:(NSString *)dateStr {
        NSDictionary *weekDic = @{@"0":@"周日",@"1":@"周一",@"2":@"周二",@"3":@"周三",@"4":@"周四",@"5":@"周五",@"6":@"周六"};
        NSInteger week = [NSDate week:dateStr];
        NSString *weekKey = [NSString stringWithFormat:@"%ld",(long)week];
        return weekDic[weekKey];
    }
    
    /// 获取日
    + (NSInteger)day:(NSString *)dateStr {
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setDateFormat:@"yyyy-MM-dd"];
        [dateFormatter setLocale:[NSLocale currentLocale]];
        NSDate *startDate = [dateFormatter dateFromString:dateStr];
        NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:startDate];
        return components.day;
    }
    
    /// 获取月共有多少天
    + (NSInteger)daysInMonth:(NSString *)dateStr {
        NSDate *date = [NSDate dateWithTimeIntervalSince1970:[NSDate timeIntervalFromDateString:dateStr] / 1000];
        NSRange daysInLastMonth = [[NSCalendar currentCalendar] rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:date];
        return daysInLastMonth.length;
    }
    
    //获取当前日期
    + (NSString *)currentDay {
        NSDateFormatter *formater = [[ NSDateFormatter alloc] init];
        NSDate *date = [NSDate date];
        [formater setDateFormat:@"yyyy-MM-dd"];
        NSString * time = [formater stringFromDate:date];
        return time;
    }
    
    //获取当前小时
    + (NSString *)currentHour {
        NSDateFormatter *formater = [[ NSDateFormatter alloc] init];
        NSDate *curDate = [NSDate date];
        [formater setDateFormat:@"H:mm"];
        NSString * curTime = [formater stringFromDate:curDate];
        return curTime;
    }
    
    //找到两个月后的第一天~ 然后通过减一天来找到下个月的最后一天,所以,下月最后一天
    + (NSString *)nextMonthLastDay {
        NSCalendar* calendar = [NSCalendar currentCalendar];
        NSDateComponents *dateComponents = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:[NSDate date]];
        //设置日为1号
        dateComponents.day =1;
        //设置月份为后延2个月
        dateComponents.month +=2;
        NSDate * endDayOfNextMonth = [calendar dateFromComponents:dateComponents];
        //两个月后的1号往前推1天,即为下个月最后一天
        endDayOfNextMonth = [endDayOfNextMonth dateByAddingTimeInterval:-1];
        //格式化输出
        NSDateFormatter *formater = [[ NSDateFormatter alloc] init];
        [formater setDateFormat:@"yyyy-MM-dd"];
        NSString * curTime = [formater stringFromDate:endDayOfNextMonth];
        return curTime;
    }
    
    ///判断是否是今天
    + (BOOL)isToday:(NSString *)dateStr {
        BOOL isDay = NO;
        NSString *day = [NSDate timeStringWithInterval:[NSDate date].timeIntervalSince1970];
        if ([dateStr isEqualToString:day]) {
            isDay = YES;
        }
        return isDay;
    }
    
    ///判断是否是明天
    + (BOOL)isTomorrow:(NSString *)dateStr {
        BOOL isDay = NO;
        NSTimeInterval time = [NSDate date].timeIntervalSince1970 + 24 * 3600;
        NSString *day = [NSDate timeStringWithInterval:time];
        if ([dateStr isEqualToString:day]) {
            isDay = YES;
        }
        return isDay;
    }
    
    ///判断是否是后天
    + (BOOL)isAfterTomorrow:(NSString *)dateStr {
        BOOL isDay = NO;
        NSTimeInterval time = [NSDate date].timeIntervalSince1970 + 48 * 3600;
        NSString *day = [NSDate timeStringWithInterval:time];
        if ([dateStr isEqualToString:day]) {
            isDay = YES;
        }
        return isDay;
    }
    
    /// 判断是否是过去的时间
    + (BOOL)isHistoryTime:(NSString *)dateStr {
        BOOL activity = NO;
        NSTimeInterval timeInterval = [NSDate timeIntervalFromDateString: dateStr];
        NSTimeInterval currentInterval = [NSDate timeIntervalFromDateString:[NSDate currentDay]];
        if (timeInterval < currentInterval) {
            activity = YES;
        }
        return activity;
    }
    
    /// 从时间戳获取具体时间 格式:6:00
    + (NSString *)hourStringWithInterval:(NSTimeInterval)timeInterval {
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setDateFormat:@"H:mm"];
        [dateFormatter setLocale:[NSLocale currentLocale]];
        NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
        NSString *dateString = [dateFormatter stringFromDate:date];
        
        return dateString;
    }
    
    /// 从时间戳获取具体小时 格式:6
    + (NSString *)hourTagWithInterval:(NSTimeInterval)timeInterval {
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setDateFormat:@"H"];
        [dateFormatter setLocale:[NSLocale currentLocale]];
        NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
        NSString *dateString = [dateFormatter stringFromDate:date];
        return dateString;
    }
    
    /// 从毫秒级时间戳获取具体小时 格式:600
    + (NSString *)hourNumberWithInterval:(NSTimeInterval)timeInterval {
        NSString *hourStr = [self hourStringWithInterval:timeInterval / 1000];
        NSString *hourNumber = [hourStr stringByReplacingOccurrencesOfString:@":" withString:@""];
        return hourNumber;
    }
    
    /// 从时间戳获取具体日期 格式:2018-03-05
    + (NSString *)timeStringWithInterval:(NSTimeInterval)timeInterval {
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setDateFormat:@"yyyy-MM-dd"];
        [dateFormatter setLocale:[NSLocale currentLocale]];
        NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval];
        NSString *dateString = [dateFormatter stringFromDate:date];
        return dateString;
    }
    
    /// 根据具体日期获取时间戳(毫秒)
    + (NSTimeInterval)timeIntervalFromDateString:(NSString *)dateStr {
        //要精确到毫秒2018-01-01 与 2018-01-01 00:00 都要转换成2018-01-01 00:00:00
        if (dateStr.length == 10) {
            dateStr = [dateStr stringByAppendingString:@" 00:00:00"];
        } else if (dateStr.length == 16) {
            dateStr = [dateStr stringByAppendingString:@":00"];
        }
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
        [dateFormatter setLocale:[NSLocale currentLocale]];
        NSDate *date = [dateFormatter dateFromString:dateStr];
        NSTimeInterval interval = [date timeIntervalSince1970] * 1000;
        return interval;
    }
    
    /// 获取当前天的后几天的星期
    + (NSString *)getWeekAfterDay:(NSInteger)day {
        NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday) fromDate:[NSDate date]];
        NSInteger currentWeek = components.weekday - 1;
        NSDictionary *weekDic = @{@"0":@"日",@"1":@"一",@"2":@"二",@"3":@"三",@"4":@"四",@"5":@"五",@"6":@"六"};
        NSInteger week = currentWeek + day;
        if (week >= 7) {
            week -= 7;
        }
        NSString *key = [NSString stringWithFormat:@"%ld",(long)week];
        return weekDic[key];
    }
    
    
    /// 获取当前天的后几天的日
    + (NSString *)getDayAfterDay:(NSInteger)day {
        NSTimeInterval time = [NSDate date].timeIntervalSince1970 + 24 * 3600 * day;
        NSString *date = [NSDate timeStringWithInterval:time];
        NSInteger dayNum = [self day:date];
        NSString *dayStr = [NSString stringWithFormat:@"%ld",(long)dayNum];
        return dayStr;
    }
    
    /// 获取当前月的后几月
    + (NSString *)getMonthAfterMonth:(NSInteger)Month {
        NSDate *currentDate = [NSDate date];
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"yyyy-MM"];
        
        NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
        NSDateComponents *lastMonthComps = [[NSDateComponents alloc] init];
        //    [lastMonthComps setYear:1]; // year = 1表示1年后的时间 year = -1为1年前的日期,month day 类推
        [lastMonthComps setMonth:Month];
        NSDate *newdate = [calendar dateByAddingComponents:lastMonthComps toDate:currentDate options:0];
        NSString *dateStr = [formatter stringFromDate:newdate];
        return dateStr;
    }
    
    @end
    
    2、UI实现
    示例页面

    接下来讲下页面构成,其他的实现起来都差不多,主要是利用CollectionView

    示例页面构成

    蓝色星期视图是个独立的View,红色是CollectionView,有自定义的组头UICollectionReusableView和cell

    首先要设计个model,满足我们展示视图的需要,比如CalendarModel,这个model属性看情况自定义:

    CalendarModel.h:

    #import <Foundation/Foundation.h>
    
    //哪一天
    typedef NS_ENUM(NSInteger, DayType) {
        Other = 0,
        Today,                    /**< 今天 */
        Tomorrow,                 /**< 明天 */
        AfterTomorrow             /**< 后天 */
    };
    
    
    @interface ReplaceCalendarModel : NSObject
    
    @property (nonatomic, strong) NSString *time;                     /**< 时间戳 */
    @property (nonatomic, strong) NSString *year;
    @property (nonatomic, strong) NSString *month;
    @property (nonatomic, strong) NSString *day;
    @property (nonatomic, strong) NSString *week;
    @property (nonatomic, assign) DayType dayType;                    /**< 是否是今天,明天,后天 */
    @property (nonatomic, assign) BOOL isSelected;                    /**< 是否被选择 */
    
    @end
    

    组头UICollectionReusableView就一个Label就可以了,展示年月,cell就是两个Label,展示日和其他信息,然后根据model来展示组头和cell就可以了,这两个实现起来比较简单,比较复杂的是collectionView的数据源处理,接下来我主要记下这个

    首先看页面要展示多少个月的日期,像这个页面展示的是当前月当前日到下一个月月底的时间,在initWithFrame里面初始化CollectionView的数据源:

        //今天的日期为开始和下个月最后一日为结束
        self.dataSource = [NSMutableArray array];
        //当前月
        NSMutableArray *currentMonth = [self createMonthDataInday:[NSDate currentDay]];
        //下个月
        NSMutableArray *nextMonth = [self createMonthDataInday:[NSDate nextMonthLastDay]];
        //初始化数据源的组
        [self.dataSource addObject:currentMonth];
        [self.dataSource addObject:nextMonth];
    
    //根据某一日数据初始化该月数据源
    - (NSMutableArray *)createMonthDataInday:(NSString *)dateStr {
        NSMutableArray *monthData = [NSMutableArray array];
        NSString *yearStr = [NSString stringWithFormat:@"%ld",[NSDate year:dateStr]];
        NSString *monthStr = [NSString stringWithFormat:@"%ld",[NSDate month:dateStr]];
        NSString *monthStr02 = [NSString stringWithFormat:@"%02ld",[NSDate month:dateStr]];
        //第一天的星期
        NSInteger firstDayWeek = [NSDate week:[NSString stringWithFormat:@"%@-%@-01",yearStr,monthStr]];
        //如果是当前月,"第一天星期几"就以今天的星期为开始
        if ([NSDate isToday:dateStr]) {
            firstDayWeek = [NSDate week:dateStr];
        }
        //这个月的天数
        NSInteger days = [NSDate daysInMonth:dateStr];
        //每天的数字
        for (int i = 0; i < 42; i++) {
            CalendarModel *model = [[CalendarModel alloc] init];
            model.year = yearStr;
            model.month = monthStr;
            model.dayType = Other;
            //在第一天之前的格子为空
            if (i < firstDayWeek) {
                model.day = @"";
            } else if ( i > days + firstDayWeek - 1) {
                //最后一天之后的,不添加进数据源
                continue;
            } else {
                //其他的都是有效数据
                model.day = [NSString stringWithFormat:@"%ld", i - firstDayWeek + 1];
                model.date = [NSString stringWithFormat:@"%@-%@-%@",yearStr,monthStr02,[NSString stringWithFormat:@"%02ld", i - firstDayWeek + 1]];
                //如果是过去的时间,就跳出循环,不添加进数据源
                if ([NSDate isHistoryTime:model.date]) {
                    continue;
                }
                //今天明天后天
                if ([NSDate isToday:model.date]) {
                    model.dayType = Today;
                    model.isSelected = YES;
                } else if ([NSDate isTomorrow:model.date]) {
                    model.dayType = Tomorrow;
                } else if ([NSDate isAfterTomorrow:model.date]) {
                    model.dayType = AfterTomorrow;
                }
            }
            [monthData addObject:model];
        }
        
        return monthData;
    }
    
    3、第三方库推荐

    FSCalendar

    4、值得注意的坑

    相关文章

      网友评论

        本文标题:iOS日历界面

        本文链接:https://www.haomeiwen.com/subject/ferrhqtx.html