最近手頭上一個(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”