iOS日歷界面

最近開發的產品中,有很多日歷相關的需求,所以整理一下開發日歷相關的東西。
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、值得注意的坑

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容