1. 程式人生 > >IOS 往手機日曆裡增,刪,查,該事件提醒

IOS 往手機日曆裡增,刪,查,該事件提醒

之前對這個塊也不是很熟悉,然後在一個專案上使用過,所以這裡就記下筆記,僅供參考。

準備工作

1.需要在工程中引入EventKit框架,#import <EventKit/EventKit.h>

2.引入了此框架後,我們可以用來作業系統日曆和提醒事項,這兩個app都是IOS系統自帶的功能。

3.這裡有個EKEventStore,相當於一個數據庫身份。因為使用比較頻繁,建議建立成單例模式。

4.新增日曆訪問許可權,IOS10之後需要往info.plist裡面配置日曆訪問許可權(NSCalendarsUsageDescription

 

日曆許可權檢測

//日曆許可權檢測
- (void)checkCalendarAuthorizationCompletion:(void(^)(BOOL granted, NSError *error))completion
{
    //EKEventStore
    //檢查日曆授權狀態
    /*
     EKAuthorizationStatusNotDetermined = 0, //未進行授權選擇
     EKAuthorizationStatusRestricted, //未授權,且使用者無法更新,如家長控制情況下
     EKAuthorizationStatusDenied, //使用者拒絕App使用
     EKAuthorizationStatusAuthorized, //已授權,可使用
     */
    EKAuthorizationStatus authorizationStatus = [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent];
    if (authorizationStatus == EKAuthorizationStatusNotDetermined || authorizationStatus == EKAuthorizationStatusDenied) {
        //未進行授權選擇
        [[self sharedEKEventStore] requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError * _Nullable error) {
            if (granted) {
                DLog(@"授權成功");
                if (completion) {
                    completion(true,nil);
                }
            }else{
                DLog(@"授權失敗,error:%@",error);
                if (completion) {
                    completion(false,error);
                }
            }
        }];
    }else if (EKAuthorizationStatusRestricted == authorizationStatus){
        if (completion) {
            completion(false,nil);
        }
    }else if (EKAuthorizationStatusAuthorized == authorizationStatus){
        if (completion) {
            completion(true,nil);
        }
    }
}

 

新增日曆事件

//新增日曆事件
- (void)addCalendarStartDate:(NSString *)startDate addEndDate:(NSString *)endDate alarms:(NSArray *)alarmArray title:(NSString *)title
{
    EKEvent *event = [EKEvent eventWithEventStore:[self sharedEKEventStore]];
    event.title = title;
    event.startDate = [NSDate dateWithString:startDate formatString:@"YYYY-MM-dd HH:mm:ss"];
    event.endDate = [NSDate dateWithString:endDate formatString:@"YYYY-MM-dd HH:mm:ss"];
    // 是否設定全天
    event.allDay = NO;
    //新增提醒
    if (alarmArray && alarmArray.count > 0){
        for (NSNumber *timeValue in alarmArray) {
            //如果需要設定提前提醒,需要設定一個負數時間值,單位秒s,根據實際需求轉換單位
            [event addAlarm:[EKAlarm alarmWithRelativeOffset:-1*[timeValue integerValue]*60]];
        }
    }
    //獲取日曆型別
    EKCalendar *calendar = [self findEKCalendar:@"根據實際需求新建一個名稱即可" eventStore:[self sharedEKEventStore]];
    [event setCalendar:calendar];
    
    // 儲存日曆
    NSError *errSave;
    //EKSpanThisEvent 表示單次日曆事件 如果需要儲存重複事件需要使用EKSpanFutureEvents
    //commit 表示當前修改是否要立即提交,因為頻繁操作資料庫,建議先儲存需要新增的事件,最後一把全部提交
    [[self sharedEKEventStore] saveEvent:event span:EKSpanThisEvent commit:NO error:&errSave];
    if (errSave) {
        DLog(@"儲存失敗,%@",errSave);
    }else{
        DLog(@"儲存成功");
    }
}

 

刪除日曆事件

// 刪除日曆事件
- (BOOL)deleteCalendarStartDate:(NSString *)startDate addEndDate:(NSString *)endDate addModifytitle:(NSString *)modifytitle{
    // 獲取到此事件
    NSArray *request = [self checkToStartDate:startDate addEndDate:endDate addModifytitle:modifytitle];
    for (int i = 0; i < request.count; i ++) {
        // 刪除這一條事件
        EKEvent *event = request[i];
        //獲取日曆型別
        EKCalendar *calendar = [self findEKCalendar:@"根據實際需求新建一個名稱即可" eventStore:[self sharedEKEventStore]];
        [event setCalendar:calendar];
        NSError*error =nil;
        // commit:NO:最後再一次性提交
        [[self sharedEKEventStore] removeEvent:event span:EKSpanThisEvent commit:NO error:&error];
        
    }
    //一次提交所有操作到事件庫
    NSError *errored = nil;
    BOOL commitSuccess= [[self sharedEKEventStore] commit:&errored];
    if (errored) {
        DLog(@"刪除失敗,%@",errored);
    }else{
        DLog(@"刪除成功");
    }
    return commitSuccess;
}

 

查詢日曆事件

//查詢日曆中是否有相同的日曆事件
- (NSArray *)checkToStartDate:(NSString *)startDate addEndDate:(NSString *)endDate addModifytitle:modifytitle
{
    //查詢所有的日曆
    NSArray *tempArray = [[self sharedEKEventStore] calendarsForEntityType:EKEntityTypeEvent];
    NSMutableArray *only3D = [NSMutableArray array];
    for (int i=0; i<tempArray.count; i++) {
        EKCalendar *temCalendar = tempArray[i];
        EKCalendarType type = temCalendar.type;
        // 工作、家庭和本地日曆
        if (type == EKCalendarTypeLocal || type == EKCalendarTypeCalDAV) {
            [only3D addObject:temCalendar];
        }
    }
    
    //謂詞查詢所有符合條件的日曆事件,開始時間-結束時間之內的所有
    NSPredicate *predicate = [[self sharedEKEventStore] predicateForEventsWithStartDate:[NSDate dateWithString:startDate formatString:@"YYYY-MM-dd HH:mm:ss"] endDate:[NSDate dateWithString:endDate formatString:@"YYYY-MM-dd HH:mm:ss"] calendars:only3D];
    // 獲取到範圍內的所有事件
    NSArray *request = [[self sharedEKEventStore] eventsMatchingPredicate:predicate];
    request = [request sortedArrayUsingSelector:@selector(compareStartDateWithEvent:)];
    if (!modifytitle || [modifytitle isEqualToString:@""]) {
        return request;
    }else{
        //日曆事件標題篩選
         NSMutableArray *onlyRequest = [NSMutableArray array];
        for (int i = 0; i < request.count; i++) {
            EKEvent *event = request[i];
            if (event.title && [event.title isEqualToString:modifytitle]) {
                [onlyRequest addObject:event];
            }
        }
        return onlyRequest;
    }
}

 

修改日曆事件

這裡並沒有直接修改日曆的介面,所以我們需要變向的達到要求。先做一下刪除操作,然後再新增。

- (BOOL)updateCalendarStartDate:(NSString *)startDate addEndDate:(NSString *)endDate addModifytitle:(NSString *)modifytitle
{
    //1.檢測是否有相同的日曆事件
    NSArray *searchResult = [self checkToStartDate:scheduled.startTime addEndDate:scheduled.endTime addModifytitle:scheduled.title];
    //2.判斷該日曆事件是否為新增/修改,還是直接刪除
      if (searchResult.count>0) {
               //1.刪除相同日曆事件
               [self deleteCalendarStartDate:scheduled.startTime addEndDate:scheduled.endTime addModifytitle:scheduled.title];
               //2.新增日曆事件
               [self addCalendarStartDate:scheduled.startTime addEndDate:scheduled.endTime alarms:@[@(scheduled.scheduleNoticeTime)] title:scheduled.title];
       }else{
                //沒有相同日曆事件,新增日曆事件
                [self addCalendarStartDate:scheduled.startTime addEndDate:scheduled.endTime alarms:@[@(scheduled.scheduleNoticeTime)] title:scheduled.title];
      }
    return YES;

}

 

完整的寫入操作

- (EKEventStore *)sharedEKEventStore
{
    static EKEventStore *eventStore = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        eventStore = [[EKEventStore alloc] init];
    });
    return eventStore;
}

- (void)writeScheduledProjectToCalendar:(NSArray <ZDHCScheduledModel*>*)scheduledList
{
    [self checkCalendarAuthorizationCompletion:^(BOOL granted, NSError *error) {
        if (granted) {
    
            //建立日曆
            EKCalendar *ekCalendar = [self findEKCalendar:@"根據實際需求新建一個名稱" eventStore:[self sharedEKEventStore]];
            if (!ekCalendar) {
                NSError *err3 = nil;
                ekCalendar = [EKCalendar calendarForEntityType:EKEntityTypeEvent eventStore:[self sharedEKEventStore]];
                ekCalendar.title = @"根據實際需求新建一個名稱";
                ekCalendar.source = [self findEKSourceWitheventStore:[self sharedEKEventStore]];
                [[self sharedEKEventStore] saveCalendar:ekCalendar commit:YES error:&err3];
                if (err3) {
                    DLog(@"建立日曆失敗");
                }else{
                    DLog(@"建立日曆成功");
                }
            }
            
            [scheduledList enumerateObjectsUsingBlock:^(ZDHCScheduledModel * _Nonnull scheduled, NSUInteger idx, BOOL * _Nonnull stop) {
            
                
                //1.檢測是否有相同的日曆事件
                NSArray *searchResult = [self checkToStartDate:scheduled.startTime addEndDate:scheduled.endTime addModifytitle:scheduled.title];
                
                //2.判斷該日曆事件是否為新增/修改,還是直接刪除
                if (scheduled.status) {
                    if (searchResult.count>0) {
                        //1.刪除相同日曆事件
                        [self deleteCalendarStartDate:scheduled.startTime addEndDate:scheduled.endTime addModifytitle:scheduled.title];
                        //2.新增日曆事件
                        [self addCalendarStartDate:scheduled.startTime addEndDate:scheduled.endTime alarms:@[@(scheduled.scheduleNoticeTime)] title:scheduled.title];
                    }else{
                        //沒有相同日曆事件,新增日曆事件
                        [self addCalendarStartDate:scheduled.startTime addEndDate:scheduled.endTime alarms:@[@(scheduled.scheduleNoticeTime)] title:scheduled.title];
                    }
                }else{
                    if (searchResult.count>0) {
                        //直接刪除
                        [self deleteCalendarStartDate:scheduled.startTime addEndDate:scheduled.endTime addModifytitle:scheduled.title];
                    }
                }
                
            }];
            
            //最後一把全部提交修改
            NSError *allUpdateCommitErr;
            [[self sharedEKEventStore] commit:&allUpdateCommitErr];
            if (allUpdateCommitErr) {
                DLog(@"提交完畢報錯,:%@",allUpdateCommitErr);
            }else{
                DLog(@"提交成功");
            }
        }
    }];
    
}

- (EKCalendar*) findEKCalendar:(NSString *)calendarName eventStore: (EKEventStore*)eventStore{
    NSArray<EKCalendar *> *calendars = [eventStore calendarsForEntityType:EKEntityTypeEvent];
    if (calendars != nil && calendars.count > 0) {
        for (EKCalendar *thisCalendar in calendars) {
            DLog(@"Calendar: %@", thisCalendar.title);
            if ([thisCalendar.title isEqualToString:calendarName]) {
                return thisCalendar;
            }
            if ([thisCalendar.calendarIdentifier isEqualToString:calendarName]) {
                return thisCalendar;
            }
        }
    }
    DLog(@"No match found for calendar with name: %@", calendarName);
    return nil;
}

- (EKSource*) findEKSourceWitheventStore: (EKEventStore*) eventStore{
    // if iCloud is on, it hides the local calendars, so check for iCloud first
    for (EKSource *source in eventStore.sources) {
        if (source.sourceType == EKSourceTypeCalDAV && [source.title isEqualToString:@"iCloud"]) {
            return source;
        }
    }
    
    // ok, not found.. so it's a local calendar
    for (EKSource *source in eventStore.sources) {
        if (source.sourceType == EKSourceTypeLocal) {
            return source;
        }
    }
    return nil;
}

  到此完美結束。