关于Calendar和Reminder(日历和提醒)编程指南

Event Kit框架使你能访问用户的Calendar.app和Reminders.app信息。虽然这是两个不同的app,但是他们使用相同的框架处理数据。类似地,存储这些数据的数据库,被称为日历数据库,持有calendar和reminder信息。

Event Kit除了允许你检索用户已经存在的calendar和reminder数据外,还允许你创建新的事件和提醒。另外,还允许你修改和删除事件和提醒。更高级的任务,例如添加警报或指定重复发生的事件,也可以。

一、Reading and Writing Calendar Events:
使用EKEventStore类来获取、创建、修改和删除事件,使用EKEvent类来表示事件。
1、连接到Event Store:
实例化EKEventStore对象:
EKEventStore *store=[[EKEventStore alloc] initWithAccessToEntityTypes:EKEntityMaskEvent];
一个EKEventStore对象需要一段明显的时间来初始化和释放。因此,你不应该为每个事件相关的任务都初始化和释放一个单独的event store。取而代之的,在你的应用加载时,初始化一个event store,并且重复使用它。
event store必须不能在其它的Event Kit对象之前被release,否则未定义的行为可能发生。

2、检索事件
有2个方法检索事件。使用predicate或search query,将返回0或多个事件来匹配给定的查询条件。使用唯一标识符将返回一个单独的事件。
注意:检索的事件不一定按照年代排序。要通过日期排序一组EKEvent对象,调用sortedArrayUsingSelector:方法,并使用selector为compareStartDateWithEvent:方法。

3、使用Predicates:
EKEventStore的方法eventsMatchingPredicate:检索所有的在你提供的日期范围内的事件。

注意:虽然eventsMatchingPredicate:方法接收NSPredicate类型的参数,但是这个NSPredicate必须是通过EKEventStore的方法predicateForEventsWithStartDate:endDate:calendars:方法创建的。

// Get the appropriate calendar
NSCalendar *calendar = [NSCalendar currentCalendar];
 
// Create the start date components
NSDateComponents *oneDayAgoComponents = [[NSDateComponents alloc] init];
oneDayAgoComponents.day = -1;
NSDate *oneDayAgo = [calendar dateByAddingComponents:oneDayAgoComponents
                                              toDate:[NSDate date]
                                             options:0];
 
// Create the end date components
NSDateComponents *oneYearFromNowComponents = [[NSDateComponents alloc] init];
oneYearFromNowComponents.year = 1;
NSDate *oneYearFromNow = [calendar dateByAddingComponents:oneYearFromNowComponents
                                                   toDate:[NSDate date]
                                                  options:0];
 
// Create the predicate from the event store's instance method
NSPredicate *predicate = [store predicateForEventsWithStartDate:oneDayAgo
                                                        endDate:oneYearFromNow
                                                      calendars:nil];
 
// Fetch all events that match the predicate
NSArray *events = [store eventsMatchingPredicate:predicate];

你可以为predicateForEventsWithStartDate:endDate:calendars:方法指定一组EKCalendar对象作为calendars的参数。你可以使用evnet store的calendarsForEntityType:方法 获得用户的calendars.传递nil值告诉方法从用户所有的calendars中获取数据。

因为eventsMatchingPredicate:方法是同步的,所以应该在另外的线程执行。

4、使用唯一标识符
使用EKEventStore的方法eventWithIdentifier:来获取事件。如果这是一个重复发生的事件,这个方法将返回第一个将要发生的事件。你可以通过EKEvent的eventIdentifier属性来获得唯一标识符。

5、创建和修改事件
使用EKEvent类的eventWithEventStore:方法创建一个新的事件
你可以编辑新事件或你之前获得的事件的细节.包括:
1)title属性
2)startDate和endDate属性
3)calendar属性:事件关联的日历
4)alarm属性
5)recurrenceRules属性:默认是不重复的


6、保存和移除事件
如果你要修改用户的日历数据库,必须得到用户的确认。
你对事件所做的更改在你保存它们之前不会永久有效。使用EKEventStore的方法saveEvent:span:commit:error:方法来保存事件。使用removeEvent:span:commit:error:来移除事件。
如果你正在保存一个重复发生的事件,你的更改可以通过指定span参数为EKSpanFutureEvents来将其应用到所有将来发生的事件。同样的,通过指定span参数为EKSpanFutureEvents,你可以移除所有将来发生的事件。

注意:如果你传递NO给commit参数,确保你之后调用commit:方法来永久保存你的更改。

7、执行事件批处理操作:
EKEventStore的enumerateEventsMatchingPredicate:usingBlock:方法允许你执行所有匹配Predicate的事件的批处理操作。你提供的operation是一个block,其类型为EKEventSearchCallback。
typedef void (^EKEventSearchCallback)(EKEvent *event,BOOL *stop)
这个方法同样也会消耗一段时间,建议在另外的线程执行。

二、Reading and Writing Reminders:
Reminder是绑定到特定时间或位置的任务。他们和Calendar事件类似,但是可以被标记为完成,并且可能不需要span一个精确的时间周期。
因为EKReminder继承自EKCalendarItem,因此你可以在一个reminder上执行如同你在事件上执行的相同的方法,例如addAlarm:或通过addRecurrenceRule:来设置recurrence规则.
1、检索Reminders:
要访问Reminders,还是实例化EVEventStore,传递EKEntityMaskReminder 而不是EKEntityMaskEvent
EKEventStore *store=[[EKEventStore alloc] initWithAccessToEntityTypes:EKEntityMaskReminder];

1)使用Predicate检索:
你可以调用fetchRemindersMatchingPredicate:completion:方法。传递的Predicate使用下面方法创建:
a)predicateForInCompleteRemindersWithDueDateStarting:ending:calendars:来查找未完成的reminders
b)predicateForCompleteRemindersWithCompletionDateStarting:ending:calendars:来查找已经完成的reminders
c)predicateForRemindersInCalendars:来查找所有的reminders

你可以在completion block中遍历找到的EKReminder对象
例子:
NSPredicate *predicate = [store predicateForRemindersInCalendars:nil];
 
[store fetchRemindersMatchingPredicate:predicate completion:^(NSArray *reminders) {
    for (EKReminder *reminder in reminders) {
        // do something for each reminder
    }
}];

2)使用唯一标识符
calendarItemWithIdentifier:方法可以查找所有的calendar item(包括Reminder和Events),而eventWithIdentifier:只查询events。

2、创建和编辑Reminders:
你可以使用EKEventStore的reminderWithEventStore:类方法来创建reminder,title和calendar属性是必须的。
startDateComponents和dueDateComponents属性。
要完成一个reminder,设置completed属性为YES,这回自动地设置completionDate为当前日期。

3、保存和移除Reminders:
这也需要用户确认。
saveReminder:commit:error:方法和removeReminder:commit:error:方法。
记住,在你保存你的reminder之前,必须设置title和calendar属性。

三、配置Alarms
不管当前运行的是哪个程序,alarm会作为一个通知来到前台来提醒用户。如果一个alarm被设置到一个日历事件中,那么通知来自于Calendar.app;如果alarm被设置到一个reminder中,那么通知来自于Reminders.app。Alarms可以是基于时间的,在某个特定的时间发射,或基于位置的,当经过某个迪兰围栏(geofence)时发射。
事件和Reminder都可以设置alarm。

1、Attaching and Removing Alarms:
addAlarm:方法。Alarm可以使用绝对日期和相对日期创建。
removeAlarm:方法来移除。
在OS X中,你可以触发一个alarm动作。例如,设置:
1)emailAddress属性来发送一封邮件
2)soundName属性来播放一个声音
3)url属性来打开一个URL。

2、设置Geofences:
注意:OS X和iOS都支持Geofence,但是他们在移动设备上更有效。
一个geofence是一个虚拟的包围一个地理位置的border,当穿越它时,可以触发一个事件的alarm。作为一个开发者,你需要控制center的纬度和经度,和geofence的半径。
调用locationWithTItle:方法来创建一个结构化的location。要设置经度和纬度 coordinates,传递一个CLLocation给结构化的location的geoLocation属性。radius属性为0表示系统默认的半径;单位为米。
这对Reminders很有实际意义。

四、创建重复发生的事件:
重复发生的规则使用EKRecurrenceRule类来表示。
calendar event和reminder都可以使用Recurrence。

1、使用基本的规则:
你可以创建一个简单recurrence规则,使用daily、weekly、monthly或yearly,使用initRecurrenceWithFrequency:interval:end:方法。你可以在这个方法中提供3个值:
1)recurrence frequency:这个值是EKRecurrenceFrequency类型,指示recurrence是daily、weekly、monthly或yearly.
2)recurrence interval: 这是一个大于0的整数,来制定重复的间隔。例如,如果recurrence rule是weekly,并且recurrence interval是1,然后重复模式为每星期都重复。如果recurrence interval是3,那么是每3个星期重复一次。
3)recurrence end: 这是个可选参数,是一个EKRecurrenceEnd类的对象,它指示recurrence rules何时终止。

2、使用复合规则:
使用
initRecurrenceWithFrequency:interval:daysOfTheWeek:daysOfTheMonth:monthsOfTheYear:weeksOfTheYear::daysOfTheYear:setPostions:end:
方法。在这个方法里,你可以提供额外的可选值的组合来描述一个特定的规则:

days   为 一组 EKRecurrenceDayOfWeek对象,      可以和除了daily recurrence以外的rule组合  ,例子:一个包含EKTuesday和EKFriday对象的array将创建一个每个周二和周五发生的规则。

monthDays  为一组非0的NSNumber对象,从-31到31.负值表示从当前月的结尾向前计算,可以和Monthly 规则组合,例子:一个包含1和-1的array,将创建每个月第一天和最后一天发生的规则。

months 为一组1到12的NSNumber对象,对应格里高里日历的月份,可以和Yearly组合。

weeksOfTheYear 为一组非0的NSNumber对象,从-53到53。负值表示从一年的最后一个礼拜向前计算。可以和Yearly组合。

daysOfTheYear 为一组非0NSNumber对象,从-366到366. 可以和Yearly组合

setPositions: 为一组非0NSNumber对象,从-366到366.负值表示反向计算。可以和除了daily规则之外的所有规则组合。例子:如果你提供了一个包含1和-1的array到一个yearly规则,并且指定了周一到周五的daysOfTheWeek,那么重复发生只在第一周和最后一周。也就是说这个参数过滤其它规则的过滤器。

你可以提供任意的number参数值,不能应用的参数会被忽略。If you provide a value for more than one of the above parameters, the recurrence occurs only on days that apply to all provided values.

一旦你创建了一个recurrence rule,你可以应用它到事件和reminder上,通过EKCalendarItem的addRecurrenceRule:实例方法。

五、观察外部的日历数据库的更改:
1、注册通知:
一个EKEventStore对象发布EKEventStoreChangedNotification通知。
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(storeChanged:)
                                             name:EKEventStoreChangedNotification
                                           object:eventStore];

2、响应通知:
当你收到一个EKEventStoreChangedNotification通知时,可能你已经获取到的对象--例如EKEvent、EKReminder或EKCalendar--已经改变了。这些更改的效果基于事件是添加、修改还是删除。
1)如果一个event被添加了。这不影响你现有的获取到的事件和reminder,但是新添加的event可能不会显示给用户(因为你是在新添加的event之前获取的数据)。
2)如果event被修改了或删除了,表示那个event的EKEvent和EKReminder对象的属性将变得过期。

因为在你收到EKEventStoreChangedNotification通知时,也就是在日历数据库发生更改时,你的本地数据经常被无效或未完成,所以你应该重新获取你当前日期范围的events。如果你正在修改一个event并且你不想重新获取它除非绝对需要这样做,你可以调用日历事件的refresh方法。如果这个方法返回YES,你可以继续使用这个日历事件,否则,你应该重新获取它。

六、提供事件的Interface
注意:这章讨论的Event Kit UI 框架只适用于iOS,在OS X中,你需要自己创建界面。

Event Kit UI 框架提供了两种类型的View Controller来管理事件:
1)EKEventViewController: 来显示一个已经存在的事件
2)EKEventEditViewController: 来创建、修改和删除事件


1、显示Event数据:
使用EKEventViewController。你需要在显示view controller之前,设置event属性,和其它显示属性。例子:
    EKEventViewController *eventViewController = [[EKEventViewController alloc] init];
    eventViewController.event = myEvent;
    eventViewController.allowsEditing = YES;
    navigationController = [[UINavigationController alloc] initWithRootViewController:eventViewController];

你需要指定一个delegate给event view controller,在用户完成查看事件时接收通知。代理遵循EKEventViewDelegate协议并且必须实现eventViewController:didCompleteWithAction:方法。

2、修改Event数据:
使用EKEventEditViewController和EKEventEditViewDelegate协议。像创建EKEventViewController一样创建EKEventEditViewController,除了你必须设置eventStore属性(设置event属性为可选的)
1)如果event属性为nil,那么用户创建一个新的event,在默认的calendar并保存它到指定的event store。
2)如果event属性不为nil,那么用户编辑这个event。这个event必须存在于指定的event store。否则发生异常。
例子:
    EKEventEditViewController* controller = [[EKEventEditViewController alloc] init];
    controller.eventStore = myEventStore;
    controller.editViewDelegate = self;
    [self presentModalViewController:controller animated:YES];

代理函数:eventEditViewController:didCompleteWithAction: 一般在这个函数中取消modal的view controller。
action参数指定用户是cancel还是save,还是delete。
原文地址:https://www.cnblogs.com/zsw-1993/p/4879389.html