iOS系统日历的获取、创建、删除和修改

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,过滤节假日)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容