iOS EventKit日歷事件操作 開發(fā)筆記

最近手頭上一個(gè)日程管理的項(xiàng)目里有一個(gè)功能是做事務(wù)提醒的,原本是想用本地推送來(lái)實(shí)現(xiàn),但是無(wú)奈本地推送數(shù)量有限制,最多不能超過(guò)64條。如果改用遠(yuǎn)程推送來(lái)實(shí)現(xiàn),那是最好的了,但是資源、條件不是很完備(畢竟是學(xué)生項(xiàng)目,要申請(qǐng)開發(fā)者賬號(hào)還得增加后臺(tái)同學(xué)工作量等等),最后選擇了用系統(tǒng)日歷來(lái)完成。在此做了一些總結(jié),方便日后查閱。

了解EventKit框架

事件庫(kù)框架授權(quán)訪問(wèn)用戶的 Calendar.app 和 Reminders.app 應(yīng)用的信息。盡管是用兩個(gè)不同的應(yīng)用顯示用戶的日歷和提醒數(shù)據(jù),但確是同一個(gè)框架維護(hù)這份數(shù)據(jù)。他們使用相同的庫(kù)(EKEventStore)處理數(shù)據(jù)。

該框架除了允許檢索用戶已經(jīng)存在的calendar和reminder數(shù)據(jù)外,還允許創(chuàng)建新的事件和提醒。更高級(jí)的任務(wù),諸如添加鬧鐘或指定循環(huán)事件,也可以使用事件庫(kù)完成。

使用

準(zhǔn)備

在項(xiàng)目中導(dǎo)入EventKit框架和EventKitUI框架



在使用到這個(gè)框架的文件中import進(jìn)來(lái):

#import <EventKit/EventKit.h>
#import <EventKitUI/EventKitUI.h>

創(chuàng)建一個(gè)事件庫(kù)實(shí)例

EKEventStore *eventStore = [[EKEventStore alloc] init];
因?yàn)镋KEventStore就像數(shù)據(jù)庫(kù)一樣,頻繁的開啟,關(guān)閉會(huì)影響效率,所以如果你的程序需要頻繁操作日歷和提醒,建議僅生成該對(duì)象一次,僅用一個(gè)對(duì)象進(jìn)行操作。因此項(xiàng)目里面我單獨(dú)寫了一個(gè)類封裝對(duì)日歷的操作,并用單例創(chuàng)建EKEventStore實(shí)例。

授權(quán)

iOS10以后獲得授權(quán)要在plist文件中進(jìn)行設(shè)置:添加權(quán)限字符串訪問(wèn)日歷:NSCalendarsUsageDescription 訪問(wèn)提醒事項(xiàng):NSRemindersUsageDescription


EKEntityType有兩類

EKEntityTypeEvent日歷事件 ,   EKEntityTypeReminder//提醒事項(xiàng)
- (void)calendarAuthority{
//獲取授權(quán)狀態(tài)
    EKAuthorizationStatus eventStatus = [EKEventStore  authorizationStatusForEntityType:EKEntityTypeEvent];
    //用戶還沒(méi)授權(quán)過(guò)
    if(eventStatus ==EKAuthorizationStatusNotDetermined){
        //提示用戶授權(quán),調(diào)出授權(quán)彈窗
        [[[EKEventStore alloc]init] requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError * _Nullable error) {
            if(granted){
                NSLog(@"允許");
            }else{
                NSLog(@"拒絕授權(quán)");
            }
        }];
    }
    //用戶授權(quán)不允許
    else if (eventStatus == EKAuthorizationStatusDenied){
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"當(dāng)前日歷服務(wù)不可用" message:@"您還沒(méi)有授權(quán)本應(yīng)用使用日歷,請(qǐng)到 設(shè)置 > 隱私 > 日歷 中授權(quán)" preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *action = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        }];
        [alert addAction:action];
        [self presentViewController:alert animated:YES completion:nil];
    }
}
//還有兩種授權(quán)狀態(tài):
//EKAuthorizationStatusAuthorized用戶已經(jīng)允許授權(quán)
//EKAuthorizationStatusRestricted,未授權(quán),且用戶無(wú)法更新,如家長(zhǎng)控制情況下

檢查授權(quán)狀態(tài)我們使用的是EKEventStore authorizationStatusForEntityType:類方法,而調(diào)出系統(tǒng)日歷事件授權(quán)彈窗使用的卻是EKEventStore的實(shí)例方法requestAccessToEntityType:

找到對(duì)應(yīng)的日歷源

日歷源它是EKSource的實(shí)例對(duì)象,可以理解為是按照日歷類型去分類的一些組。在模擬器下:


模擬器截圖

在這個(gè)圖里,我們看到有兩個(gè)日歷源,一個(gè)是“ON MY IPHONE”另一個(gè)是"OTHER"。這里我們打個(gè)斷點(diǎn)查看所有日歷源:



可以看到日歷數(shù)據(jù)庫(kù)中第一個(gè)日歷源的真正名稱為Default(是一個(gè)Local源),而后面一個(gè)名稱為Other。在模擬器中顯示的第一個(gè)日歷源的名稱只是一個(gè)便于用戶理解的別名。(但我目前還不是很清楚subscribe類型的那一項(xiàng)是什么)

然后在真機(jī)上,第一個(gè)日歷源的名字是ICLOUD,第二個(gè)日歷源是“其他”。我們可以看到無(wú)論是真機(jī)還是模擬器,每個(gè)日歷源里都可以有若干個(gè)日歷。

舉例獲取iCloud源:

EKSource *localSource = nil;
for (EKSource *source in _eventStore.sources){
//獲取iCloud源。這里可以只通過(guò)日歷源的title來(lái)查找,不過(guò)加上對(duì)類型的檢查就是雙保險(xiǎn)
   if (source.sourceType == EKSourceTypeCalDAV && [source.title isEqualToString:@"iCloud"]){
    //把獲取到的iCloud源保存起來(lái)
    localSource = source;
   }
}

其他幾種日歷源:

typedef NS_ENUM(NSInteger, EKSourceType) {
    EKSourceTypeLocal,
    EKSourceTypeExchange,
    EKSourceTypeCalDAV,
    EKSourceTypeMobileMe,
    EKSourceTypeSubscribed,
    EKSourceTypeBirthdays
};

(Local是本地的源;iCloud是遠(yuǎn)程源,網(wǎng)絡(luò)聯(lián)網(wǎng)同步數(shù)據(jù)有點(diǎn)關(guān)系,EKSourceTypeExchange也是遠(yuǎn)程源)

獲取日歷源中指定的日歷

使用EKEventStore的- (NSArray<EKCalendar *> *)calendarsForEntityType:(EKEntityType)entityType方法,這個(gè)方法會(huì)返回一個(gè)EKCalendar的數(shù)組,里面包含符合要求的日歷(支持事件的或者支持提醒的)
原本EKSource里面有一個(gè)只讀的獲取指定日歷源中所有日歷的屬性,但現(xiàn)在已經(jīng)廢棄掉了~@property(nonatomic, readonly) NSSet<EKCalendar *> *calendars NS_DEPRECATED(NA, NA, 4_0, 6_0);

EKCalendar *calendar;
for (EKCalendar *ekcalendar in [_eventStore calendarsForEntityType:EKEntityTypeEvent]) {
//當(dāng)然這里也可以加上日歷源類型的檢查,像上面一樣的雙保險(xiǎn) calendar.type == EKCalendarTypeCalDAV
      if ([ekcalendar.title isEqualToString:@"小雅"] ) {
           calendar = ekcalendar;
      }
}

和上面幾種日歷源對(duì)應(yīng)的日歷類型:

typedef NS_ENUM(NSInteger, EKCalendarType) {
    EKCalendarTypeLocal,
    EKCalendarTypeCalDAV,
    EKCalendarTypeExchange,
    EKCalendarTypeSubscription,
    EKCalendarTypeBirthday
};

在系統(tǒng)日歷中創(chuàng)建自定義日歷


我們需要先找到iCloud大分類,然后才能創(chuàng)建自定義的日歷,而且創(chuàng)建時(shí),我們需要判斷是否創(chuàng)建,否則,會(huì)創(chuàng)建一些具有同樣名稱的日歷

@property (nonatomic ,strong)EKCalendar *cal;

- (EKCalendar *)cal{
    if (_cal == nil) {
        BOOL shouldAdd = YES;
        EKCalendar *calendar;
        for (EKCalendar *ekcalendar in [_eventStore calendarsForEntityType:EKEntityTypeEvent]) {
            if ([ekcalendar.title isEqualToString:@"小雅"] ) {
                shouldAdd = NO;
                calendar = ekcalendar;
            }
        }
        if (shouldAdd) {
            EKSource *localSource = nil;
            //真機(jī)
            for (EKSource *source in _eventStore.sources){
                if (source.sourceType == EKSourceTypeCalDAV && [source.title isEqualToString:@"iCloud"]){//獲取iCloud源
                    localSource = source;
                    break;
                }
            }
            if (localSource == nil){
                //模擬器
                for (EKSource *source in _eventStore.sources) {//獲取本地Local源(就是上面說(shuō)的模擬器中名為的Default的日歷源)
                    if (source.sourceType == EKSourceTypeLocal){
                        localSource = source;
                        break;
                    }
                }
            }
            calendar = [EKCalendar calendarForEntityType:EKEntityTypeEvent eventStore:_eventStore];
            calendar.source = localSource;
            calendar.title = @"小雅";//自定義日歷標(biāo)題
            calendar.CGColor = [UIColor greenColor].CGColor;//自定義日歷顏色
            NSError* error;
            [_eventStore saveCalendar:calendar commit:YES error:&error];
        }
        _cal = calendar;
    }
    return _cal;
}

日歷事件查詢

要訪問(wèn)日歷中的事件,需要提供一個(gè)查詢條件,因?yàn)橛行┲貜?fù)事件是沒(méi)有盡頭的,你不可能獲取一個(gè)無(wú)限長(zhǎng)的事件列表。因此需要使用NSPredicate謂詞來(lái)進(jìn)行篩選。
- (NSPredicate *)predicateForEventsWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate calendars:(nullable NSArray<EKCalendar *> *)calendars;在這個(gè)方法里分別需要傳入:需要查詢的開始時(shí)間、截止時(shí)間、要查詢的日歷類型數(shù)組。返回符合條件的日歷事件(上限是四年內(nèi),如果開始、結(jié)束的時(shí)間跨度超過(guò)4年就截取最開始的4年的事件返回)

- (NSArray*)checkEvent{
    NSCalendar *calendar = [NSCalendar currentCalendar];
    // 創(chuàng)建起始日期組件
    NSDateComponents *oneDayAgoComponents = [[NSDateComponents alloc] init];
    oneDayAgoComponents.day = -1;
    NSDate *oneDayAgo = [calendar dateByAddingComponents:oneDayAgoComponents
                                                  toDate:[NSDate date]
                                                 options:0];  
    // 創(chuàng)建結(jié)束日期組件
    NSDateComponents *oneMonthFromNowComponents = [[NSDateComponents alloc] init];
    oneMonthFromNowComponents.month = 1;
    NSDate *oneMonthFromNow = [calendar dateByAddingComponents:oneMonthFromNowComponents
                                                       toDate:[NSDate date]
                                                      options:0];
    // 用事件庫(kù)的實(shí)例方法創(chuàng)建謂詞。表示 找出從當(dāng)前時(shí)間前一天到當(dāng)前時(shí)間的一個(gè)月后的時(shí)間范圍的所有typesArray里類型的日歷事件
    NSPredicate*predicate = [self.eventStore predicateForEventsWithStartDate:oneDayAgo endDate:oneMonthFromNow calendars:@[self.cal]];
    NSArray *eventArray = [self.eventStore eventsMatchingPredicate:predicate];
    return eventArray;
}

這樣就獲取了從當(dāng)前時(shí)間前一天到當(dāng)前時(shí)間的一個(gè)月后的事件數(shù)組。

eventArray是符合條件的日歷事件數(shù)組,數(shù)組里存的是EKEvent類型數(shù)據(jù)。關(guān)于EKEvent的一些屬性和方法,這里簡(jiǎn)單提幾個(gè),詳細(xì)的看api文檔。

+ (EKEvent *)eventWithEventStore:(EKEventStore *)eventStore 創(chuàng)建一個(gè)新的自動(dòng)釋放的事件對(duì)象
@property(nonatomic, readonly) NSString *eventIdentifier; 唯一標(biāo)識(shí)符區(qū)分某個(gè)事件.修改事件有可能
@property(nonatomic, getter=isAllDay) BOOL allDay 設(shè)置是否是全天事件
@property(nonatomic, copy) NSDate *startDate; 事件開始時(shí)間
@property(nonatomic, copy) NSDate *endDate; 結(jié)束時(shí)間
@property(nonatomic, copy, nullable) EKStructuredLocation *structuredLocation 事件里添加的位置。可以獲取到經(jīng)緯度等相關(guān)信息。
  • 事件修改
    拿到event然后對(duì)它的各個(gè)屬性賦新值就好了。在保存時(shí),從哪個(gè)EKEventStore里取出來(lái)就要存回哪個(gè)EKEventStore。

添加事件到系統(tǒng)日歷、設(shè)置重復(fù)周期、創(chuàng)建任意時(shí)間之前開始的提醒

添加的方法:- (BOOL)saveEvent:(EKEvent *)event span:(EKSpan)span commit:(BOOL)commit error:(NSError **)error
event:要添加的事件
span:設(shè)置跨度。 有兩種選擇:````EKSpanThisEvent表示只影響當(dāng)前事件,EKSpanFutureEvents表示影響當(dāng)前和以后的所有事件。比如某條重復(fù)任務(wù)修改后保存時(shí),傳EKSpanThisEvent表示值修改這一條重復(fù)事件,傳EKSpanFutureEvents```表示修改這一條和以后的所有重復(fù)事件;刪除事件時(shí),分別表示刪除這一條;刪除這一條和以后的所有。
commit:是否馬上保存事件(類似sqlite里面的“事務(wù)”)。YES表示馬上執(zhí)行,立即把此次操作提交到系統(tǒng)事件庫(kù);NO表示此時(shí)不提交,直到調(diào)用commit:方法時(shí)才執(zhí)行。
如果一次性操作的事件數(shù)比較少的話,可以每次都傳YES,實(shí)時(shí)更新事件數(shù)據(jù)庫(kù)。如果一次性操作的事件較多的話,可以每次傳NO,最后再執(zhí)行一次提交所有更改到數(shù)據(jù)庫(kù),把原來(lái)的更改(不管是添加還是刪除)全部提交到數(shù)據(jù)庫(kù)。
error:出錯(cuò)信息,如果沒(méi)出錯(cuò)值是nil。

- (void)addEventNotifyWithTitle:(NSString*)title dateString:(NSString*)dateString startSection:(NSString *)startSection endSection:(NSString *)endSection repeatIndex:(NSInteger)repeatindex alarmSettings:(NSArray *)remindIndexs note:(NSString*)notes{
//創(chuàng)建一個(gè)新事件
    EKEvent *event = [EKEvent eventWithEventStore:self.eventStore];
    //1.標(biāo)題
    event.title = title;
    //2.開始時(shí)間
    event.startDate = [self.dateFormatter dateFromString:[dateString stringByAppendingString:[self sectionStartTime:startSection]]];
    //3.結(jié)束時(shí)間
    event.endDate = [self.dateFormatter dateFromString:[dateString stringByAppendingString:[self sectionEndTime:endSection]]];
    //4.重復(fù)規(guī)則
    EKRecurrenceRule *rule = [self repeatRule:repeatindex currentDate:dateString];
    if (rule != nil) {
        event.recurrenceRules = @[rule];
    }else{
        event.recurrenceRules = nil;
    }
    event.notes = notes;//6.備注
    [event setAllDay:NO];//設(shè)置全天
    //5.設(shè)置提醒
    for (int i = 0; i < remindIndexs.count; i++) {
        EKAlarm *alarm = [self alarmsSettingWithIndex:[remindIndexs[i] intValue]];
        if (alarm == nil) {
            event.alarms = nil;
            break;
        }
        [event addAlarm:alarm];
    }
    [event setCalendar:self.cal];//設(shè)置日歷類型
    //保存事件
    NSError *err = nil;
    if([self.eventStore saveEvent:event span:EKSpanThisEvent commit:NO error:nil]){//注意這里是no,在外部調(diào)用完這個(gè)add方法之后一定要commit
        NSLog(@"創(chuàng)建事件到系統(tǒng)日歷成功!,%@",title);
    }else{
        NSLog(@"創(chuàng)建失敗%@",err);
    }
}

//重復(fù)規(guī)則
- (EKRecurrenceRule *)repeatRule:(NSInteger)repeatIndex currentDate:(NSString*)dateString{
    NSDate *currentDate = [self.dateFormatter dateFromString:[dateString stringByAppendingString:@"0000"]];
    NSCalendar * gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
    NSDateComponents *components = [gregorian components:NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond fromDate:currentDate];
    components.year += 1;
    NSDate *recurrenceEndDate = [gregorian dateFromComponents:components];//高頻率:每天、每?jī)商臁⒐ぷ魅?    NSDateComponents *components2 = [gregorian components:NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond fromDate:currentDate];
    components2.year += 3;
    NSDate *recurrenceEndDate2 = [gregorian dateFromComponents:components2];//低頻率:每周、每月、每年

    EKRecurrenceRule * rule;
    switch (repeatIndex) {
        case 0://每天
            rule = [[EKRecurrenceRule alloc]initRecurrenceWithFrequency:EKRecurrenceFrequencyDaily interval:1 daysOfTheWeek:nil daysOfTheMonth:nil monthsOfTheYear:nil weeksOfTheYear:nil daysOfTheYear:nil setPositions:nil end:[EKRecurrenceEnd recurrenceEndWithEndDate:recurrenceEndDate]];
            break;
        case 1://每?jī)商?            rule = [[EKRecurrenceRule alloc]initRecurrenceWithFrequency:EKRecurrenceFrequencyDaily interval:2 daysOfTheWeek:nil daysOfTheMonth:nil monthsOfTheYear:nil weeksOfTheYear:nil daysOfTheYear:nil setPositions:nil end:[EKRecurrenceEnd recurrenceEndWithEndDate:recurrenceEndDate]];
            break;
        case 2://每周
            rule = [[EKRecurrenceRule alloc]initRecurrenceWithFrequency:EKRecurrenceFrequencyWeekly interval:1 daysOfTheWeek:nil daysOfTheMonth:nil monthsOfTheYear:nil weeksOfTheYear:nil daysOfTheYear:nil setPositions:nil end:[EKRecurrenceEnd recurrenceEndWithEndDate:recurrenceEndDate2]];
            break;
        case 3://每月
            rule = [[EKRecurrenceRule alloc]initRecurrenceWithFrequency:EKRecurrenceFrequencyMonthly interval:1 daysOfTheWeek:nil daysOfTheMonth:nil monthsOfTheYear:nil weeksOfTheYear:nil daysOfTheYear:nil setPositions:nil end:[EKRecurrenceEnd recurrenceEndWithEndDate:recurrenceEndDate2]];
            break;
        case 4://每年
            rule = [[EKRecurrenceRule alloc]initRecurrenceWithFrequency:EKRecurrenceFrequencyYearly interval:1 daysOfTheWeek:nil daysOfTheMonth:nil monthsOfTheYear:nil weeksOfTheYear:nil daysOfTheYear:nil setPositions:nil end:[EKRecurrenceEnd recurrenceEndWithEndDate:recurrenceEndDate2]];
            break;
        case 5://工作日
            rule = [[EKRecurrenceRule alloc]initRecurrenceWithFrequency:EKRecurrenceFrequencyDaily interval:1 daysOfTheWeek:[NSArray arrayWithObjects:[EKRecurrenceDayOfWeek dayOfWeek:2],[EKRecurrenceDayOfWeek dayOfWeek:3],[EKRecurrenceDayOfWeek dayOfWeek:4],[EKRecurrenceDayOfWeek dayOfWeek:5],[EKRecurrenceDayOfWeek dayOfWeek:6],nil] daysOfTheMonth:nil monthsOfTheYear:nil weeksOfTheYear:nil daysOfTheYear:nil setPositions:nil end:[EKRecurrenceEnd recurrenceEndWithEndDate:recurrenceEndDate]];
            break;
        case 6:
            rule = nil;
            break;
        default:
            rule = nil;
            break;
    }
    return rule;
}

//@[@"當(dāng)事件發(fā)生時(shí)",@"5分鐘前",@"15分鐘前",@"30分鐘前",@"1小時(shí)前",@"1天前",@"不提醒"]
- (EKAlarm *)alarmsSettingWithIndex:(int )remindIndex{
    EKAlarm *alarm;
    switch (remindIndex) {
        case 0:
            alarm = [EKAlarm alarmWithRelativeOffset:0];
            break;
        case 1:
            alarm = [EKAlarm alarmWithRelativeOffset:- 60.0 * 5];
            break;
        case 2:
            alarm = [EKAlarm alarmWithRelativeOffset:- 60.0 * 15];
            break;
        case 3:
            alarm = [EKAlarm alarmWithRelativeOffset:-60.0 * 30];
            break;
        case 4:
            alarm = [EKAlarm alarmWithRelativeOffset:-60.0 * 60];
            break;
        case 5:
            alarm = [EKAlarm alarmWithRelativeOffset:-60.0 * 60 * 24];
            break;
        case 6:
            alarm = nil;
            break;
        default:
            alarm = nil;
            break;
    }
    return alarm;
}
  • 設(shè)置事件重復(fù)
    主要用到EKRecurrenceRule這個(gè)類來(lái)設(shè)置重復(fù)規(guī)則
- (instancetype)initRecurrenceWithFrequency:(EKRecurrenceFrequency)type
                        interval:(NSInteger)interval 
                   daysOfTheWeek:(nullable NSArray<EKRecurrenceDayOfWeek *> *)days
                  daysOfTheMonth:(nullable NSArray<NSNumber *> *)monthDays
                 monthsOfTheYear:(nullable NSArray<NSNumber *> *)months
                  weeksOfTheYear:(nullable NSArray<NSNumber *> *)weeksOfTheYear
                   daysOfTheYear:(nullable NSArray<NSNumber *> *)daysOfTheYear
                    setPositions:(nullable NSArray<NSNumber *> *)setPositions
                             end:(nullable EKRecurrenceEnd *)end;

一個(gè)個(gè)參數(shù)來(lái)看:
type:重復(fù)規(guī)則的頻率

typedef NS_ENUM(NSInteger, EKRecurrenceFrequency) {
    EKRecurrenceFrequencyDaily,//按天
    EKRecurrenceFrequencyWeekly,//按周
    EKRecurrenceFrequencyMonthly,//按月
    EKRecurrenceFrequencyYearly//按年
};

interval:間隔,必須大于0
days:一周中的哪幾天
monthDays:一個(gè)月中的哪幾號(hào)
months:一年中哪幾個(gè)月
weeksOfTheYear:一年中的哪幾周
daysOfTheYear:一年中的哪幾天
setPositions:規(guī)則外的設(shè)置
end:結(jié)束規(guī)則。有兩種:按次數(shù)和按時(shí)間。按時(shí)間:[EKRecurrenceEnd recurrenceEndWithEndDate:recurrenceEndDate]表示recurrenceEndDate該時(shí)間后不再計(jì)算;按次數(shù):[EKRecurrenceEnd recurrenceEndWithOccurrenceCount:10]表示十次

舉個(gè)例子:
1.每?jī)商靾?zhí)行一次:type: EKRecurrenceFrequencyDaily;interval:2
2.每工作日?qǐng)?zhí)行:type: EKRecurrenceFrequencyDaily;interval:1;days為星期一...星期五,具體參數(shù)設(shè)置:[NSArray arrayWithObjects:[EKRecurrenceDayOfWeek dayOfWeek:2],[EKRecurrenceDayOfWeek dayOfWeek:3],[EKRecurrenceDayOfWeek dayOfWeek:4],[EKRecurrenceDayOfWeek dayOfWeek:5],[EKRecurrenceDayOfWeek dayOfWeek:6],nil]
3.每?jī)芍苤芤粓?zhí)行一次:type:EKRecurrenceFrequencyWeekly;interval:2;days:[NSArray arrayWithObjects:[EKRecurrenceDayOfWeek dayOfWeek:2],nil]
4.每月1號(hào)執(zhí)行一次:type: EKRecurrenceFrequencyMonthly;interval:1;monthDays:@[@1];

添加重復(fù):event.recurrenceRules = rule數(shù)組;或者使用- (void)addRecurrenceRule:(EKRecurrenceRule *)rule;逐個(gè)規(guī)則添加

  • 創(chuàng)建任意時(shí)間之前開始的提醒
    使用EKAlarm鬧鐘來(lái)做提醒功能。
    創(chuàng)建方法有兩種:
 + (EKAlarm *)alarmWithAbsoluteDate:(NSDate *)date; 設(shè)置絕對(duì)時(shí)間
 + (EKAlarm *)alarmWithRelativeOffset:(NSTimeInterval)offset; 設(shè)置相對(duì)時(shí)間(相對(duì)event的start date),而且這個(gè)相對(duì)時(shí)間的基本單位是秒,設(shè)置負(fù)值表示事件前提醒,設(shè)置正值是事件發(fā)生后提醒

舉例:事件五分鐘前提醒

EKAlarm *alarm = [EKAlarm alarmWithRelativeOffset:- 60.0 * 5];
[event addAlarm:alarm];//逐個(gè)鬧鐘添加到事件

也可以event.alarms = 鬧鐘數(shù)組一次性添加所有提醒。

設(shè)置了提醒后,我們打開iOS系統(tǒng)自帶的日歷App,會(huì)發(fā)現(xiàn)只會(huì)顯示2個(gè)提醒,看不到多余的提醒.但是實(shí)際測(cè)試發(fā)現(xiàn)全部提醒都可以工作,而且我們可以在Mac的日歷程序中看到所有的提醒。

刪除系統(tǒng)日歷事件

刪除的方法:- (BOOL)removeEvent:(EKEvent *)event span:(EKSpan)span commit:(BOOL)commit error:(NSError **)error參數(shù)和添加事件方法差不多,使用也很簡(jiǎn)單,這里只貼一下代碼不多敘述。

NSArray *eventArray = [self checkEventWithDateString:dateStrArray startSection:startSection endSection:endSection];
    if (eventArray.count > 0) {
        for (int i = 0; i < eventArray.count; i++) {
            EKEvent * event = eventArray[i];
            [event setCalendar:self.cal];
            NSError *error = nil;
            BOOL successDelete;
            if (deleteFuture) {
                successDelete = [self.eventStore removeEvent:event span:EKSpanFutureEvents commit:NO error:&error];
            }else{
                successDelete = [self.eventStore removeEvent:event span:EKSpanThisEvent commit:NO error:&error];
            }
//            if(!successDelete) {
//                NSLog(@"刪除本條事件失敗");
//            }else{
//                NSLog(@"刪除本條事件成功,%@",error);
//            }
        }
        //一次提交所有操作到事件庫(kù)
        [self commitEvent];
    }

- (void)commitEvent{
    NSError *error =nil;
    BOOL commitSuccess= [self.eventStore commit:&error];
    if(!commitSuccess) {
        NSLog(@"一次性提交事件失敗,%@",error);
    }else{
        NSLog(@"成功一次性提交事件,%s",__func__);
    }
}

其他

還有一個(gè)問(wèn)題,就是在你修改事件的過(guò)程中如果事件發(fā)生了變化。
日歷發(fā)生變化時(shí)都會(huì)發(fā)出EKEventStoreChangedNotification通知,調(diào)用EKEvent的refresh方法即可刷新這個(gè)事件確保事件還是可用的,另外它還會(huì)刷新事件的屬性值,已經(jīng)修改過(guò)的屬性并不會(huì)被更新。(如果refresh方法返回NO那么這個(gè)事件已經(jīng)被刪除掉或者已經(jīng)是無(wú)效的,不應(yīng)該再使用它)。
關(guān)于這個(gè)問(wèn)題,因?yàn)槲疫€沒(méi)有實(shí)際使用到,所以點(diǎn)到即止。

項(xiàng)目中遇到的一個(gè)問(wèn)題:如果app正在使用時(shí)切換到系統(tǒng)“設(shè)置”,把a(bǔ)pp的日歷授權(quán)關(guān)了,這時(shí)候app會(huì)崩掉。不清楚原因,崩潰時(shí)斷點(diǎn)顯示“SIGKILL”

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,732評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,214評(píng)論 3 426
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,781評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,588評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,315評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,699評(píng)論 1 327
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,698評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,882評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,441評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,189評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,388評(píng)論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,933評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,613評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,023評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,310評(píng)論 1 293
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,112評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,334評(píng)論 2 377

推薦閱讀更多精彩內(nèi)容