本文主要内容是如何一步一步使用EventKit
在iOS设备中添加日历,并在日历中添加事件和提醒事项。 git
源代码Githubgithub
EKAlarm
类用于提供操做系统日历通知的相关接口,通知时间既能够是绝对时间,也能够是相对时间。spa
实例化方法操作系统
//绝对时间 + (EKAlarm *)alarmWithAbsoluteDate:(NSDate *)date; //相对时间 + (EKAlarm *)alarmWithRelativeOffset:(NSTimeInterval)offset;
属性相关类code
EKStructuredLocation
通知的位置属性,包括标题title
,地理位置geoLocation
和半径radius
orm
EKAlarmProximity
是一个标记为进入或者离开的枚举类型继承
typedef NS_ENUM(NSInteger, EKAlarmProximity) { EKAlarmProximityNone, EKAlarmProximityEnter, //进入 EKAlarmProximityLeave //离开 };
EKAlarmType
记录通知类型的枚举属性教程
typedef NS_ENUM(NSInteger, EKAlarmType) { EKAlarmTypeDisplay, //展现信息 EKAlarmTypeAudio, //播放声音 EKAlarmTypeProcedure, //打开网址 EKAlarmTypeEmail //发邮件 };
首先,经过``能够进行系统受权,使用下面的方法接口
//申请权限 - (void)requestAccessToEntityType:(EKEntityType)entityType completion:(EKEventStoreRequestAccessCompletionHandler)completion NS_AVAILABLE(10_9, 6_0); //获取当前权限 + (EKAuthorizationStatus)authorizationStatusForEntityType:(EKEntityType)entityType NS_AVAILABLE(10_9, 6_0);
枚举量包括 实例类型EKEntityType
和 实例蒙版EKEntityMask
事件
EKCalendar
和 EKEventStore
的关系能够理解为,一个EKEventStore
能够包含多个EKCalendar
。
这个类的类型枚举变量是
typedef NS_ENUM(NSInteger, EKCalendarType) { EKCalendarTypeLocal, EKCalendarTypeCalDAV, EKCalendarTypeExchange, EKCalendarTypeSubscription, EKCalendarTypeBirthday };
其中,EKCalendarTypeCalDAV
和 EKCalendarTypeExchange
是两种邮箱帐户的事件同步类型,EKCalendarTypeBirthday
是一个内置的生日日历,EKCalendarTypeLocal
和设备同步,EKCalendarTypeSubscription
则是用于本地的同步类型。
EKEvent
与EKReminder
同样继承于EKCalendarItem
。
上面对于头文件的分析,有助于咱们实际编写代码。
要点一 添加受权描述
首先须要在项目的plist文件中,加入申请系统日历使用的描述,不然没法发起受权请求。键为NSCalendarsUsageDescription
,值为提交请求的描述。
而后判断当前的受权状态
+ (BOOL)accessForEventKit:(EKEntityType)type { return [EKEventStore authorizationStatusForEntityType:type] == EKAuthorizationStatusAuthorized; }
固然,能够增长额外的判断,好比当状态为EKAuthorizationStatusDenied
的时候,能够提醒用户前往系统设置
打开系统日历的受权,咱们上面这段代码只是简单的判断是否拥有系统日历的操做权限,当拥有权限时,进行业务代码,若是尚未进行过受权,则用下满的方法吊起受权
+ (void)accessForEventKitType:(EKEntityType)type result:(void(^)(BOOL))result { EKEventStore* store = [[EKEventStore alloc] init]; [store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError * _Nullable error) { if (!granted) { NSLog(@"%@",error); } if (result) { result(granted); } }]; }
这里是否封装成方法均可以,我这里写成类方法是为了方便读者在任意位置粘贴代码和调用。
受权完成之后就能够进行业务代码的操做了。
首先使用下面的代码获取系统日历中所有的日历
+ (NSArray*)calendarWithType:(EKEntityType)type { EKEventStore* store = [[EKEventStore alloc] init]; return [store calendarsForEntityType:type]; }
这里一样区分事件的种类,得到结果以下,每一个苹果帐号的日历内容不尽相同
"EKCalendar <0x1740a5580> {title = Birthdays; type = Birthday; allowsModify = NO; color = #8295AF;}",
"EKCalendar <0x1740a5dc0> {title = Calendar; type = CalDAV; allowsModify = YES; color = #1BADF8;}",
"EKCalendar <0x1740a56a0> {title = U65e5U5386; type = CalDAV; allowsModify = YES; color = #1BADF8;}",
"EKCalendar <0x1740a5640> {title = U5de5U4f5c; type = CalDAV; allowsModify = YES; color = #63DA38;}",
"EKCalendar <0x1740a55e0> {title = U5bb6U5ead; type = CalDAV; allowsModify = YES; color = #FFCC00;}"
EKCalendar
和 EKCalendarItem
并非双向的可读取关系,经过EKCalendarItem
实例能够获取它的EKCalendar
,但咱们没法经过EKCalendar
获取系统所有的EKCalendarItem
,这样不一样的应用之间是没法相互操做事件的。
每个EKCalendarItem
拥有独一无二的calendarItemIdentifier
标识,是每一个事件的id,这个字符串是应用本身分配的,保证了每一个应用能够在添加了事件之后,针对性的进行事件修改。
你们注意到上面输出的EKCalendar
中有一个日历是禁止应用修改的,就是Birthdays
日历,由于这个日历是同步于通信录中的联系人生日的特殊日历。
EKCalendar
除了上面获取的系统已有日历,咱们也能够添加本身定义的日历
要点二 日历的calendarIdentifier存储
应用须要本身存储本身添加的日历的惟一标识,就是calendarIdentifier
,咱们这里使用NSUserDefault
来存储。
+ (void)createCalendar { NSUserDefaults * def = [NSUserDefaults standardUserDefaults]; NSString* calendarIdentifier = [def valueForKey:@"testCalendarIdentifier"]; EKCalendar* birthday = [store calendarWithIdentifier:calendarIdentifier]; //这里若是calendarIdentifier为nil,则EKCalendar也会为nil if (!birthday) { birthday = [EKCalendar calendarForEntityType:EKEntityTypeEvent eventStore:store]; birthday.title = @"test日历"; //注意calendarIdentifier自动生成的,这里要保存下来 [def setObject:birthday.calendarIdentifier forKey:@"testCalendarIdentifier"]; [def synchronize]; NSError* error; [store saveCalendar:birthday coobjectivecit:YES error:&error]; if (error) { NSLog(@"%@",error); } } }
上面的代码执行将没法添加日历,须要越过两个坑
要点三 坑
坑一是EKCalendar
须要设置EKSource
才能够添加,因此会获得下面的错误
Error Domain=EKErrorDomain Code=14 "Calendar has no source" UserInfo={NSLocalizedDescription=Calendar has no source}
然而EKSource
并不能够经过EventKit管理,也就是说,系统有哪些日历帐户,就只能用哪些,获取的办法以下:
+ (EKSource*)sourceWithType:(EKSourceType)type { EKEventStore* store = [[EKEventStore alloc] init]; EKSource *localSource = nil; NSLog(@"%@",store.sources); for (EKSource *source in store.sources) { if (source.sourceType == type) { localSource = source; break; } } return localSource; }
咱们经过遍历全部的系统Source,来匹配咱们须要的Source。
这里就引出坑二,当系统开启或关闭了iCloud日历功能的时候,Source会有不一样,例如开启iCloud日历的时候,个人设备只有一个EKSourceTypeCalDAV
类型的叫iCloud的Source和一个Other类型的Source(每一个设备可能不尽相同),但在其余没有开启的设备上,才有EKSourceTypeLocal
类型的Source,因此上面一段代码的Source匹配,可能要执行屡次,或者按照前后顺序匹配,若是单独只匹配一种类型,会有可能找不到可使用的Source。
找到Source之后,将其赋值给Calendar,注意EKCalendar
的这个属性虽然不是Readonly
,但只能在初始化日历的时候进行设置,不能再更改。
birthday.source = [[self class] sourceWithType:EKSourceTypeCalDAV]; if (!birthday.source) { birthday.source = [[self class] sourceWithType:EKSourceTypeLocal]; }
如此则可顺利添加自定义的日历。
最后一步添加事件则很简单
+ (void)addEvent { EKEvent* event = [EKEvent eventWithEventStore:store]; event.calendar = birthday; event.title = @"个人生日"; NSDateFormatter* formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"yyyy-objectivec-dd-HH-objectivec"]; event.startDate = [formatter dateFromString:@"2017-11-12-00-00"]; event.endDate = [formatter dateFromString:@"2017-11-12-23-59"]; NSError* error; [store saveEvent:event span:EKSpanThisEvent error:&error]; if (error) { NSLog(@"%@",error); } NSLog(@"%@",event.eventIdentifier); }
event设置标题、日历和起始日期便可,保存步骤中用到的EKSpanThisEvent
枚举表示只应用于当前事件,这个枚举的另外一个值EKSpanFutureEvents
表示应用到全部此事件,包括重复事件的将来的事件。
最后保存成功的话,获得的eventIdentifier
,应用能够根据须要保存在本地。
以上就是EventKit的基础教程。