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