1、Info.plist 中添加获取系统日历权限提醒
Privacy - Calendars Usage Description / 有助于您更好的管理日程
2、获取权限
/**
EKAuthorizationStatusNotDetermined = 0,// 未进行授权选择
EKAuthorizationStatusRestricted,//未授权,且用户无法更新,如家长控制情况下
EKAuthorizationStatusDenied, // 用户拒绝App使用
EKAuthorizationStatusAuthorized, // 已授权,可使用
*/
-(BOOL)getLocalCalendarAuthorization{
_eventStore = [[EKEventStore alloc]init];
EKAuthorizationStatus eventStatus = [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent];
__block BOOL allow = NO;
if(eventStatus ==EKAuthorizationStatusNotDetermined){
//用户尚未授权,提示用户授权。下边的requestAccessToEntityType:方法可以调出系统授权弹窗
[_eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
//允许
allow = YES;
}else{
//不允许
allow = NO;
}
}];
}else if(eventStatus ==EKAuthorizationStatusAuthorized){
//用户已经允许授权。作相应处理,比如查询日历里今天的所有事件..
allow = YES;
}
return allow;
}
3、获取所有的系统本地日历
/// 获取系统本地日历事件
- (NSArray *)getLocalSystemCalendarEventWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate {
NSArray * eventArray = [_eventStore calendarsForEntityType:EKEntityTypeEvent];
NSMutableArray * onlyArray = [NSMutableArray array];
for (int i=0; i<eventArray.count; i++) {
EKCalendar * tempCalendar = eventArray[i];
EKCalendarType type = tempCalendar.type;
if (type == EKCalendarTypeCalDAV) {
[onlyArray addObject:tempCalendar];
}
}
NSPredicate * predicate = [_eventStore predicateForEventsWithStartDate:startDate endDate:endDate calendars:onlyArray];
NSArray * events = [_eventStore eventsMatchingPredicate:predicate];
return events;
}
通过 event.calendar.allowsContentModifications == YES 可以过滤节假日、节气等系统添加的日历。
由于我们手动添加的数据都是可以手动编辑的,所以event的allowsContentModifications这一只读属性刚好可以用到。
4、创建日程并写入本地日历
- (void)writeToLocalCalendarAction {
EKEventStore *eventStore = [[EKEventStore alloc] init];
//事件保存到日历
//06.07 元素
//title(标题 NSString),
//location(位置NSString),
//startDate(开始时间 2016/06/07 11:14AM),
//endDate(结束时间 2016/06/07 11:14AM),
//addAlarm(提醒时间 2016/06/07 11:14AM),
//notes(备注类容NSString)
/// 06.07 时间格式
NSDateFormatter * dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setAMSymbol:@"AM"];
[dateFormatter setPMSymbol:@"PM"];
[dateFormatter setDateFormat:@"hh:mm"];
NSDate *date = [NSDate date];
NSString * s = [dateFormatter stringFromDate:date];
NSLog(@"%@",s);
/// 创建事件
EKEvent *event = [EKEvent eventWithEventStore:eventStore];
event.title = [NSString stringWithFormat:@"写入日历事件-%@",s];
event.location = @"北京app";
/// 开始时间(必须传)
event.startDate = [date dateByAddingTimeInterval:60 * 2];
/// 结束时间(必须传)
event.endDate = [date dateByAddingTimeInterval:60 * 5 * 24];
/// event.allDay = YES;//全天
/// 添加提醒
/// 第一次提醒 (几分钟后)
[event addAlarm:[EKAlarm alarmWithRelativeOffset:60.0f * -1.0f]];
/// 第二次提醒 ()
/// [event addAlarm:[EKAlarm alarmWithRelativeOffset:60.0f * -10.0f * 24]];
/// 06.07 add 事件类容备注
NSString * str = @"这是备注";
event.notes = [NSString stringWithFormat:@"%@:%@",str,s];
/// 将日历事件添加到默认的日历源中
[event setCalendar:[eventStore defaultCalendarForNewEvents]];
/// 保存日历事件
NSError *err;
[eventStore saveEvent:event span:EKSpanThisEvent error:&err];
}
5、删除单个日历和删除所有日历
/// 删除日历事件(删除单个)
/// @param eventIdentifier 事件ID(标识符)
- (BOOL)deleteCalendarEventIdentifier:(NSString *)eventIdentifier{
EKEvent *event;
NSError*error =nil;
if (eventIdentifier && ![eventIdentifier isEqualToString:@""]) {
event = [self.eventStore eventWithIdentifier:eventIdentifier];
/// commit:NO:最后再一次性提交,YES:当前提交
return [self.eventStore removeEvent:event span:EKSpanThisEvent commit:YES error:&error];
// 一次提交所有操作到事件库
// NSError *errored = nil;
// BOOL commitSuccess = [self.eventStore commit:&errored];
// return commitSuccess;
}
return NO;
}
/// 删除日历事件(可删除一段时间内的事件)
/// @param startDate 开始时间
/// @param endDate 结束时间
- (BOOL)deleteCalendarStartDate:(NSDate *)startDate addEndDate:(NSDate *)endDate {
// 获取到此事件
NSArray * eventArray = [self.eventStore calendarsForEntityType:EKEntityTypeEvent];
NSMutableArray * onlyArray = [NSMutableArray array];
for (int i=0; i<eventArray.count; i++) {
EKCalendar * tempCalendar = eventArray[i];
EKCalendarType type = tempCalendar.type;
if (type == EKCalendarTypeCalDAV) {
[onlyArray addObject:tempCalendar];
}
}
NSPredicate * predicate = [self.eventStore predicateForEventsWithStartDate:startDate endDate:endDate calendars:onlyArray];
NSArray * events = [self.eventStore eventsMatchingPredicate:predicate];
for (int i = 0; i < events.count; i ++) {
// 删除这一条事件
EKEvent *event = events[i];
NSError*error =nil;
// commit:NO:最后再一次性提交
[self.eventStore removeEvent:event span:EKSpanThisEvent commit:NO error:&error];
}
//一次提交所有操作到事件库
NSError *errored = nil;
BOOL commitSuccess = [self.eventStore commit:&errored];
return commitSuccess;
}
- (EKEventStore *)eventStore {
if (!_eventStore) {
_eventStore = [[EKEventStore alloc]init];
}
return _eventStore;
}
6、修改日历:
根据eventIdentifier 查找对应日历,没找到则重新创建,找如果有先删除在重新创建。
7、问题解决:
(1)唯一事件id(eventIdentifier)只读的问题:事件创建之后,系统自动创建eventIdentifier,而我们无法准确找到对应事件的 eventIdentifier。
@property(null_unspecified, nonatomic, readonly) NSString *eventIdentifier;
(2)获取本地日历中的日程数据数据量可能会很大,导致与服务端返回的新数据进行匹配的时候双重for循环影响效率。
解决方案
:使用allowsContentModifications属性。由于我们手动添加的数据都是可以手动编辑的,所以event的allowsContentModifications这一只读属性刚好可以用到。可以减少很多系统日历自带的event对象,比如节假日、节气等等。
event.calendar.allowsContentModifications == YES
8、其他
(1)基于地理位置的提醒
我们可以设定当用户进入或离开指定的地理位置区域时,触发日程提醒。例如当用户离开公司,提醒用户需要到超市购买日用品,作为开发者,需要确定一个经纬度以及一个半径范围。
EKAlarm *alarm = [[EKAlarm alloc] init];
EKStructuredLocation *location = [EKStructuredLocation
locationWithTitle:@"Current Location"];
location.geoLocation = [[CLLocation alloc] initWithLatitude:23.1754700000 longitude:113.4147400000];
alarm.structuredLocation = location;
alarm.proximity = EKAlarmProximityEnter;
[event addAlarm:alarm];
参考:
1、iOS 添加项目到系统日历
2、iOS增、删、改、查系统提醒事件和日历事件
3、iOS—EventKit实现app日程同步到本地日历(唯一id,过滤节假日)