iOS读/写/删除系统提醒事项和日历事件

2,272 阅读6分钟

一 了解EventKit框架。

EventKit框架使你能访问用户的Calendar(日历)和Reminder(提醒事项)信息。虽然二者在手机上是两个独立的app,但他们使用相同的库(EKEventStore)处理数据,该库管理所有event数据。该框架除了允许检索用户已经存在的calendar和reminder数据外,还允许创建新的事件和提醒。

二 连接到EKEventStore

EKEventStore 就像一个数据库,日历事件和提醒事项的数据全都在EKEventStore里存着,增删查改全都用其实例对象来管理。

日历事件:

<1.>在项目里导入EventKit框架和EventKitUI框架。
<2.>EKEventStore eventStore=[[EKEventStore alloc] initWithAccessToEntityTypes:EKEntityMaskEvent]; 一个EKEventStore对象需要一段明显的时间来初始化和释放。因此,你不应该为每个事件相关的任务都初始化和释放一个单独的event store。取而代之的,在你的应用加载时,初始化一个event store,并且重复使用它。
EKEntityType枚举 包含EKEntityTypeEvent(日历事件)和EKEntityTypeReminder(提醒事项)两种。可以在
***EKEventStore初始化时直接指定类型。也可以直接allocinit。
<3.>请求app授权。
iOS10之后,要用到某个权限必须在info.plist里指明,否则会引起崩溃和审核失败。添加权限字符串--访问日历:NSCalendarsUsageDescription访问提醒事项:NSRemindersUsageDescription

检查授权状态:

//授权状态:EKAuthorizationStatusNotDetermined 用户还没授权过。EKAuthorizationStatusAuthorized用户已经允许授权。
EKAuthorizationStatus  eventStatus = [EKEventStore  authorizationStatusForEntityType:EKEntityTypeEvent]; 
if (eventStatus == EKAuthorizationStatusNotDetermined) { 
   //用户尚未授权,提示用户授权。下边的requestAccessToEntityType:方法可以调出系统授权弹窗

} else if (eventStatus == EKAuthorizationStatusAuthorized){
  //用户已经允许授权。作相应处理,比如查询日历里今天的所有事件..
}

下边的requestAccessToEntityType调出系统的日历事件(EKEntityTypeEvent)权限弹窗

image.png

<4.>检索系统日历事件 ()

image.png

NSArray*tempA=[self.eventStore calendarsForEntityType:EKEntityTypeEvent];该方法可以得到所有的日历类型。

比如:家庭,工作,生日,中国节假日等等。你如果需要家庭,工作,iPhone日历,只需for循环挑选出需要的类型的日历放到一个数组里,然后将该数组传给谓词方法里

NSPredicate*predicate = [self.eventStorepredicateForEventsWithStartDate:startTodayDate endDate:endTodayDate calendars:typesArray];

NSArray *eventArray = [self.eventStoreeventsMatchingPredicate:predicate];

表示 找出从startTodayDate今天的开始时间到今天的结束时间endTodayDate时间范围的所有typesArray里类型的日历事件。开始和结束时间不是写死的,自己需要时间段的时间传对应的值即可。

<5>.事件EKEvent各个属性含义。
eventArray数组是刚才checkTodayEvent方法里返回的事件数组。数组里存的是EKEvent类型数据。EKEvent里属性可在EKEvent.h里查看。下边列举一些:
EKEvent *event =eventArray[i];
title:事件的标题 
notes:事件备注 
eventIdentifier:唯一标识符区分某个事件.
startDate:开始时间 
endDate: 结束时间 (特殊情况:日历里结束日期可以设置的比开始日期小。根据实际需求做对应处理。)
alarms:闹钟数组,如果event.alarms.count >0 表示设置了多个闹钟。该数组由EKAlarm组成。

<6> 闹钟EKAlarm各属性含义
EKEvent里可以设置多个提醒,alarms数组
EKAlarm*firssAlert = event.alarms.firstObject;//取出第一个闹钟
//计算出定制的第一个闹钟的具体触发时间。也就是最先提醒的那个闹钟的具体时间
NSDate *detailAlertDate = [event.startDate dateByAddingTimeInterval:firssAlert.relativeOffset];
relativeOffset=0.表示到event.startDate时提醒。- 60表示提前一分钟提醒。

<7>.重复EKRecurrenceRule规则属性含义。

重复结束于:用属性EKRecurrenceEnd表示。EKRecurrenceEnd*recurrenceEnd =rule.recurrenceEnd;
重复类型:
每5周重复。每周的二,三,四重复。结束重复时间为:20220321 。EKRecurrenceRule里的数据打印出来是:FREQ=WEEKLY;INTERVAL=5;UNTIL=20220321T155959Z;BYDAY=TU,WE,TH;WKST=SU
每4个月重复。每月的18,19,20,24,对应:FREQ=MONTHLY;INTERVAL=4;BYMONTHDAY=18,19,20,24,

<8.>受邀人EKParticipant。里边包含了受邀人的账号姓名状态等等。
<9.>位置EKStructuredLocation。事件里添加的位置。可以获取到经纬度等相关信息。

添加事件到系统日历

添加方法:

- (BOOL)saveEvent:(EKEvent*)event span:(EKSpan)span error:(NSError**)error;

EKAlarm *alarm = [EKAlarm alarmWithAbsoluteDate:[now dateByAddingTimeInterval:30]]; //现在开始30秒后提醒
EKEvent *event = [EKEvent eventWithEventStore:self.eventStore];
event.title = @"事件标题"; //标题
event.startDate = now; //开始时间
event.endDate = [now dateByAddingTimeInterval:30]; //结束时间
[event setAllDay:YES]; //设置全天
[event addAlarm:alarm]; //添加一个闹钟
[event setCalendar:[self.eventStore defaultCalendarForNewEvents]]; //默认日历类型
//保存事件
[self.eventStore saveEvent:event span:EKSpanThisEvent commit:YES error:nil];

NSError *err = nil;
if([self.eventStore saveEvent:event span:EKSpanThisEvent commit:YES error:&err]){
   NSLog(@"创建事件到系统日历成功!");
}else{
   NSLog(@"创建失败%@",err);
}

span:设置跨度。 EKSpanThisEvent:表示只影响当前事件。  EKSpanFutureEvents 表示影响当前和以后的所有事件。比如某条重复任务修改后保存时,传EKSpanThisEvent表示值修改这一条重复事件。传EKSpanFutureEvents表示修改这一条和以后的所有重复事件。删除事件时,分别表示删除这一条;删除这一条和以后的所有。

删除系统日历事件

删除方法:

- (BOOL)removeEvent:(EKEvent*)event span:(EKSpan)span commit:(BOOL)commit error:(NSError**)error;

EKEvent *event = self.eventArray[i];
[event setCalendar:[self.eventStore defaultCalendarForNewEvents]];
NSError *error = nil;
BOOL  successDelete = [self.eventStore removeEvent:event span:EKSpanFutureEvents commit:NO error:&error];
if(!successDelete) {
   NSLog(@"删除本条事件失败");
} else {
   NSLog(@"删除本条事件成功,%@",error);
}

//一次提交所有操作到事件库
NSError*error = nil;
BOOL commitSuccess = [self.eventStore commit:&error];
if(!commitSuccess) {
   NSLog(@"一次性提交删除事件是失败");
} else {
   NSLog(@"成功一次性提交删除事件,%@", error);
}

注意添加和删除时方法里都有一个 commit:(BOOL)commit 参数。yes:表示立即把此次操作提交到系统事件库,NO表示此时不提交。如果一次性操作的事件数比较少的话,可以每次都传YES,实时更新事件数据库。如果一次性操作的事件较多的话,可以每次传NO,最后再执行一次提交所有更改到数据库,把原来的更改全部提交到数据库,不管是添加还是删除。

读取提醒事项

//日历,iCloud家庭,工作,订阅,生日 image.png

//来查找所有的reminders
NSPredicate *pre = [self.eventStore predicateForRemindersInCalendars:only3A];

/异步方法。
[self.eventStore fetchRemindersMatchingPredicate:pre completion:^(NSArray *_Nullable reminders) { 
  //异步查找出提醒事项数组reminders,这里可根据需求进一步进行对数组的操作
}

reminders 数组里存的是EKReminder对象。
列举EKReminder 的一些属性:
title:标题
notes:备注
priority:优先级(NSUInteger0无级别,1级别高---9级别低(1-4高,5中等,6-9低)
completed:是否已完成
completionDate:完成时间
alarms:提醒数组(数组里是EKAlarm对象, 可以获得跟闹钟相关的数据,如具体时间,偏移秒数...)

添加一条提醒事项

EKAlarm *alarm = [EKAlarm alarmWithAbsoluteDate:[now dateByAddingTimeInterval:30]];
EKReminder *reminder = [EKReminder reminderWithEventStore:es];
reminder.title = @"提醒的标题";
NSCalendar *cal = [NSCalendar currentCalendar];
[cal setTimeZone:[NSTimeZone systemTimeZone]];
NSInteger flags = NSYearCalendarUnit | NSMonthCalendarUnit |NSDayCalendarUnit |NSHourCalendarUnit | NSMinuteCalendarUnit |NSSecondCalendarUnit;
reminder.startDateComponents = [cal components:flags fromDate:[now dateByAddingTimeInterval:30]];//开始时间
reminder.dueDateComponents = [cal components:flags fromDate:[now dateByAddingTimeInterval:30]]; //结束时间
reminder.completionDate = [now dateByAddingTimeInterval:30];
[reminder setCalendar:[self.eventStore defaultCalendarForNewReminders]];
reminder.priority = 1; //优先级
[reminder addAlarm:alarm];

NSError *err = nil;
if([self.eventStore saveReminder:reminder commit:YES error:&err]){
   NSLog(@"创建成功!");
} else {
   NSLog(@"创建失败%@",err);
}
//记得commit。

删除提醒事项

[reminderArray enumerateObjectsUsingBlock:^(id_Nonnull obj, NSUInteger idx, BOOL *_Nonnullstop) {
   EKReminder *reminder = (EKReminder*)obj; 
   /*如果你有不止一个EKReminder需要删除,好的做法是不要一个一个的提交,而是全部删除,在最后一次性提交。这个也适用于增加新的事件到存储器中*/
   NSError *error = nil;
   BOOL success = [self.eventStore removeReminder:reminder commit:NO error:&error];
   if(!success) {
      NSLog(@"删除错误");
   } else { 
      NSLog(@"本次删除成功");
   }
}];

//一次性全部提交修改
NSError *commitErr = nil;
BOOL commitSuccess = [self.eventStore commit:&commitErr];
if(!commitSuccess) {
  NSLog(@"提交到事件库错误");
} else {
  NSLog(@"成功一次性全部提交到事件库");
}