前一段時間由於工做須要實現了一個能夠簽到的日曆,來記錄一下實現的思路
效果如圖:
這裏的基本需求是:
1,顯示用戶某個月的簽到狀況,已經簽到的日子打個圈,沒有簽到且在某個時間範圍內的能夠簽到,其餘的只能看。
2,服務器只會返回這個月用戶簽到日子的時間戳數組和能夠簽到的時間範圍,剩下的日子就是沒有簽到的。
3,顯示跟普通的日曆同樣便可,上面是「一二三四五六日」,下面是對應的日期。
4,能夠切換到當前日期以前的月份。
根據需求,基本思路是:
用一個pageViewController做爲基本控制器,控制顯示某月簽到狀況的viewController;
(爲何用pageViewController呢?由於需求要求能夠切換月份,用pageViewController方便複用,並且每月可能都須要請求服務器數據,將網絡請求寫到viewController當中更好控制整個生命週期)
每一個viewController是一個月的狀況,上面放一個自定義的calendarView,
用UICollectionView實現CalendarView,每一個cell顯示一天日期,
定義幾個狀態分別表示可簽到,不可簽到,已簽到等狀態,而後配置好cell的狀態就好了。
界面的設計大概就是這個樣子,難點是日曆的邏輯和數據配置問題
首先,要根據服務器返回的時間戳肯定今天的日期及當月的DateComponents(防止用戶修改系統時間來做弊,用NSDateComponents表示某個月方便比較);
而後,根據當月的dateComponents顯示日曆,
這裏須要注意的是,要想辦法獲取到當月有多少天和當月的第一天是星期幾!!!
獲取當月第一天的dateComponents:
NSDateComponents *firstDayComponents = [[NSDateComponents alloc] init];
firstDayComponents.year = monthComponents.year;
firstDayComponents.month = monthComponents.month;
firstDayComponents.day = 1;
而後就能夠用NSCalendar的api提供的方法
NSDate *firstDay = [self.calendar dateFromComponents:firstDayComponents];
NSDateComponents *dateComponents = [self.calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitWeekday fromDate:firstDay];
NSRange range = [self.calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:firstDay];//指定日期的day在month中的位置
因此range的length就是當月有多少天,dateComponents.weekday就是當月第一天是周幾。
這裏又須要注意,NSCalendar的firstWeekday和minimumDaysInFirstWeek屬性:
- (NSCalendar *)calendar {
if (!_calendar) {
_calendar = [NSCalendar currentCalendar];
_calendar.firstWeekday = 2;//1是週日,2是週一,以此類推
_calendar.minimumDaysInFirstWeek = 1;//表示一個月的最初一週若是少於這個值,則算爲上個月的最後一週,大約等於則算本月第一週,用1就好
}
return _calendar;
}
有了這些就能夠算出當月第一天以前有多少空白cell:
self.frontBlankCount = dateComponents.weekday - self.calendar.firstWeekday;
if (self.frontBlankCount < 0) {
//這裏,由於weekday週日是1,因此判斷下
self.frontBlankCount += 7;
}
而後算出當月最後有多少空白:
for (int i = 0; i < self.frontBlankCount; i++) {
//這裏是讓每月最開始的地方按需求空白
[self.dataArray addObject:@""];
}
for (int i = 0; i < range.length; i++) {
NSString *text = [NSString stringWithFormat:@"%@", @(i + 1)];
[self.dataArray addObject:text];
}
NSInteger weeks = 0; //每月有幾周
NSInteger aaa = self.dataArray.count / 7;
NSInteger bbb = self.dataArray.count % 7;
if (bbb == 0) {
weeks = aaa;
} else {
weeks = aaa + 1;
}
self.backBlankCount = weeks * 7 - self.dataArray.count;
到這裏日曆就能夠完整的顯示出來了。
剩下的就是匹配簽到數據:
只須要一個for循環,判斷日曆日期是否在服務器返回的簽到日期之中就能夠了;
若是須要判斷是否能夠簽到,加入一個時間戳大小的判斷就行。
最後再reloadData就能夠了~~~