請你們忽略圖片質量哈,我這軟件弄出來的質量不高.最終實現的就是在客戶端可以簽到功能,使用了覺得大神封裝的日曆類,FSCalendar,附github地址:FSCalendargit
個人demo基本就是一個使用FSCalendar的一個樣例,可是直接使用中會有一些坑,話很少說,直接上代碼github
1.首先安裝FSCalendar數組
pod 'FSCalendar', '~> 2.7.9'
複製代碼
2.建立一個新類,導入FSCalendar和系統事件庫EventKit緩存
#import "FSCalendar.h"
//用來讀取,修改和建立日曆上的事件
#import <EventKit/EventKit.h>
複製代碼
3.重寫loadView方法,建立FSCalendarbash
//建立日曆類
FSCalendar *calendar = [[FSCalendar alloc] initWithFrame:CGRectMake(0, self.navigationController.navigationBar.frame.size.height, self.view.bounds.size.width - 50, 300)];
calendar.backgroundColor = [UIColor whiteColor];
calendar.dataSource = self;
calendar.delegate = self;
//日曆語言爲中文
calendar.locale = [NSLocale localeWithLocaleIdentifier:@"zh-CN"];
//容許多選,能夠選中多個日期
calendar.allowsMultipleSelection = YES;
//若是值爲1,那麼週日就在第一列,若是爲2,週日就在最後一列
calendar.firstWeekday = 1;
//週一\二\三...或者頭部的2017年11月的顯示方式
calendar.appearance.caseOptions = FSCalendarCaseOptionsWeekdayUsesSingleUpperCase|FSCalendarCaseOptionsHeaderUsesUpperCase;
[self.view addSubview:calendar];
self.calendar = calendar;
複製代碼
4.根據建立的日曆類作初始化設置服務器
#pragma mark - <配置日曆>
- (void)calendarConfig{
//建立系統日曆類
self.gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
//獲取日曆要顯示的日期範圍
NSArray *timeArray = [ViewController getStartTimeAndFinalTime];
//設置最小和最大日期(在最小和最大日期以外的日期不能被選中,日期範圍若是大於一個月,則日曆可翻動)
self.minimumDate = [self.dateFormatter dateFromString:timeArray[0]];
self.maximumDate = [self.dateFormatter dateFromString:timeArray[1]];
self.calendar.accessibilityIdentifier = @"calendar";
//title顯示方式
self.calendar.appearance.headerDateFormat = @"yyyy年MM月";
//關閉字體自適應,設置字體大小\顏色
self.calendar.appearance.adjustsFontSizeToFitContentSize = NO;
self.calendar.appearance.subtitleFont = [UIFont systemFontOfSize:8];
self.calendar.appearance.headerTitleColor = [UIColor whiteColor];
self.calendar.appearance.weekdayTextColor = [UIColor whiteColor];
self.calendar.appearance.selectionColor = [UIColor orangeColor];
//日曆頭部顏色
self.calendar.calendarHeaderView.backgroundColor = themeColor;
self.calendar.calendarWeekdayView.backgroundColor = themeColor;
}
複製代碼
5.實現FSCalendar數據源方法網絡
#pragma mark - FSCalendarDataSource
//日期範圍(最小)
- (NSDate *)minimumDateForCalendar:(FSCalendar *)calendar
{
return self.minimumDate;
}
//日期範圍(最大)
- (NSDate *)maximumDateForCalendar:(FSCalendar *)calendar
{
return self.maximumDate;
}
複製代碼
6.(重點)簽到邏輯 app
- (void)viewDidLoad {
[super viewDidLoad];
//日曆配置
[self calendarConfig];
//1.加載緩存中的的日期,並選中這些日期
[self getCache];
//2.從網絡獲取其簽到結果,若是發現請求的結果中存在沒有被選中,就將其選中,並加載到緩存中
[self getSign];
}
複製代碼
上述兩個方法的具體實現大體思路爲:async
//加載緩存
- (void)getCache{
//從緩存中先把數據取出來
NSString *key = [NSString stringWithFormat:@"arrayDate"];
NSMutableArray *cache = [[NSUserDefaults standardUserDefaults] objectForKey:key];
//容許用戶選擇,實際上是容許系統來選中籤到日期
self.calendar.allowsSelection = YES;
self.calendar.allowsMultipleSelection = YES;
if (cache.count) {//若是cache裏面有數據
//選中日期,只有不在選中之列的纔去選中它
for (NSInteger i = 0; i<cache.count; i++) {
if (![self.calendar.selectedDates containsObject:cache[i]]) {
[self.calendar selectDate:cache[i]];
}
}
}else{//若是cache裏面沒有數據,說明第一次啓動
//建立個可變數組儲存進緩存
NSMutableArray *cache = [NSMutableArray array];
[[NSUserDefaults standardUserDefaults] setValue:cache forKey:key];
[[NSUserDefaults standardUserDefaults] synchronize];
}
//選擇完畢後關閉可選項,不讓用戶本身點
self.calendar.allowsSelection = NO;
}
//點擊簽到按鈕的Action
- (void)signInAction{
//假設在這裏網絡請求籤到成功,成功後須要從新請求籤到全部結果
if (_count>31) {//此處的判斷僅爲本demo臨時使用,正式使用中能夠根據具體狀況刪除此if else判斷
NSLog(@"別點了");
return;
}else if (!_count){
_count = 1;
}
NSString *dateStr = [NSString stringWithFormat:@"2017-11-%ld",_count];
_count++;
[self.signInList addObject:dateStr];
[self getSign];
}
//從網絡獲取全部簽到結果
- (void)getSign{
//配置日期緩存的key
NSString *key = [NSString stringWithFormat:@"arrayDate"];
//在這裏僞裝網絡請求全部的簽到結果(signInList)成功了
NSLog(@"%@",_signInList);
//獲取簽到總數量
self.SignCount = _signInList.count;
//常見臨時數組dataArrayCache,用於存放簽到結果(可能有的人以爲這一步不須要,可是我們假設的簽到結果裏面只有純日期,正式項目中可不必定如此)
NSMutableArray *dataArrayCache = [NSMutableArray array];
if (self.SignCount) {//若是請求的數據有效
for (NSString *dateStr in _signInList) {
//把全部簽到數據取出來添加進臨時數組
NSDate *date = [self.dateFormatter dateFromString:dateStr];
if(date){
[dataArrayCache addObject:date];
}
}
//用偏好設置保存簽到數據到本地緩存
[[NSUserDefaults standardUserDefaults] setValue:dataArrayCache forKey:key];
[[NSUserDefaults standardUserDefaults] synchronize];
//保存後從新加載緩存數據
[self getCache];
}
}
//獲取日曆範圍,讓日曆出現時就知道該顯示哪一個月了哪一頁了(根據系統時間來獲取)
+(NSArray *)getStartTimeAndFinalTime{
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"YYYY-MM-dd"];
NSDate *datenow = [NSDate date];
NSString *currentTimeString = [formatter stringFromDate:datenow];
NSDate *newDate=[formatter dateFromString:currentTimeString];
double interval = 0;
NSDate *firstDate = nil;
NSDate *lastDate = nil;
NSCalendar *calendar = [NSCalendar currentCalendar];
BOOL OK = [calendar rangeOfUnit:NSCalendarUnitMonth startDate:& firstDate interval:&interval forDate:newDate];
if (OK) {
lastDate = [firstDate dateByAddingTimeInterval:interval - 1];
}else {
return @[@"",@""];
}
NSString *firstString = [formatter stringFromDate: firstDate];
NSString *lastString = [formatter stringFromDate: lastDate];
//返回數據爲日曆要顯示的最小日期firstString和最大日期lastString
return @[firstString, lastString];
}
複製代碼
7.關於FSCalendar的代理方法基本不須要實現,由於簽到通常不容許用戶點擊,若是有特殊需求的話,代價能夠加上,更多的能夠去delegate中尋找須要用的字體
#pragma mark - FSCalendarDelegate
//手動選中了某個日期(本demo暫時被隱藏)
- (void)calendar:(FSCalendar *)calendar didSelectDate:(NSDate *)date atMonthPosition:(FSCalendarMonthPosition)monthPosition
{
NSLog(@"did select %@",[self.dateFormatter stringFromDate:date]);
}
//當前頁被改變,日曆翻動時調用(本demo暫時沒用到)
- (void)calendarCurrentPageDidChange:(FSCalendar *)calendar
{
NSLog(@"did change page %@",[self.dateFormatter stringFromDate:calendar.currentPage]);
}
複製代碼
8.顯示農曆:將LunarFormatter拖進項目,FSCalendar的demo中也有,本文demo中也有,主要在數據源方法中使用
//數據源方法,根據是否顯示節日和農曆
- (NSString *)calendar:(FSCalendar *)calendar subtitleForDate:(NSDate *)date
{
if (self.showsEvents) {//若是要求顯示節日
EKEvent *event = [self eventsForDate:date].firstObject;
if (event) {
return event.title;
}
}
if (self.showsLunar) {//若是要求顯示農曆
return [self.lunarFormatter stringFromDate:date];
}
return nil;
}
//加載節日到日曆中
- (void)loadCalendarEvents
{
__weak typeof(self) weakSelf = self;
EKEventStore *store = [[EKEventStore alloc] init];
//請求訪問日曆
[store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
//容許訪問
if(granted) {
NSDate *startDate = self.minimumDate;
NSDate *endDate = self.maximumDate;
NSPredicate *fetchCalendarEvents = [store predicateForEventsWithStartDate:startDate endDate:endDate calendars:nil];
NSArray<EKEvent *> *eventList = [store eventsMatchingPredicate:fetchCalendarEvents];
NSArray<EKEvent *> *events = [eventList filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(EKEvent * _Nullable event, NSDictionary<NSString *,id> * _Nullable bindings) {
return event.calendar.subscribed;
}]];
dispatch_async(dispatch_get_main_queue(), ^{
if (!weakSelf) return;
weakSelf.events = events;
[weakSelf.calendar reloadData];
});
} else {
// Alert
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"權限錯誤" message:@"獲取節日事件須要權限呀大寶貝!" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:alertController animated:YES completion:nil];
}
}];
}
//根據日期來顯示事件
- (NSArray<EKEvent *> *)eventsForDate:(NSDate *)date
{
NSArray<EKEvent *> *events = [self.cache objectForKey:date];
if ([events isKindOfClass:[NSNull class]]) {
return nil;
}
NSArray<EKEvent *> *filteredEvents = [self.events filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(EKEvent * _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
return [evaluatedObject.occurrenceDate isEqualToDate:date];
}]];
if (filteredEvents.count) {
[self.cache setObject:filteredEvents forKey:date];
} else {
[self.cache setObject:[NSNull null] forKey:date];
}
return filteredEvents;
}
複製代碼
9.最後獲取日曆權限須要在info.plist文件配置Privacy - Calendars Usage Description獲取日曆使用權限
10.demo地址:github.com/TynnPassBy/… install後再啓動項目,有任何疑問能夠在下方留言,我會盡力幫助你們.
感謝~