[iOS]日历和提醒编程指南(Calendar and Reminders Programming Guide)

分析:事件提醒开发包(EventKit)由事件库、事件源、日历和事件/提醒组成,他们的关系是:事件库用于直接操作日历数据库,日历数据库中的数据按事件源、日历和事件/提醒三级进行分类组织。每个事件源对应一个准帐户,该帐户下可以有多个日历,日历分两类,一类是用于存储事件的日历,一类是用于存储提醒的日历。这里所说的存储,实际就是分类,反过来的,根据子项对父项进行分类。就如两口缸,一口装水,一口沙子一样,这个缸就是上面提及的日历,水相当于事件,沙子相当于提醒。一户人家的院子里可以摆好多口缸,这个院子就相当于帐户,有两个默认帐户,一个是Local,一个是Other。帐户的类型,还可能有iCloud或Gmail帐号等,一般是邮箱附带的,所以就默认对应着该邮箱地址了。就像 大户人家的总管,管好每户的院子,还有每个院子里的缸一样,事件库直接管理所有的帐户和日历,还有日历下的事件或提醒。管理包括增加、修改、查询、删除(CURD)。
  • 日历和提醒入门(Introduction to Calendars and Reminders) 

事件库框架授权访问用户的 Calendar.app 和 Reminders.app 应用的信息。尽管是用两个不同的应用显示用户的日历和提醒数据,但确是同一个框架维护这份数据。同样地,存储这份数据的数据库叫做日历数据库,同时容纳日历和提醒信息。

The Event Kit framework grants access to users’ Calendar.app and Reminders.app information. Although two different apps display users’ calendar and reminder data, the same framework manipulates the data. Similarly, the database that stores this data, called the Calendar database, holds both calendar and reminder information.

图示 1-1 事件库结构 Figure I-1 Event Kit architecture

[iOS]日历和提醒编程指南(Calendar and Reminders Programming Guide)

事件库不但允许你的应用获取用户已经存在的日历及提醒数据,而且它可以让你的应用为任何日历创建新的事件和提醒。另外,事件库让用户可以编辑和删除他们的事件和提醒(整体叫做“日历项”)。更高级的任务,诸如添加闹钟或指定循环事件,也可以使用事件库完成。如果日历数据库有来自你的应用外部的更改发生,事件库可以通过通知监测到,这样你的应用可以做出适当的响应。使用事件库对日历项所做的更改会自动地同步到相关的日历(CalDAV - 是一种效率手册同步协议,有些效率手册 如 Apple iCal、Mozilla Lightning/Sunbird 使用这一协议使其信息能与其它效率手册 如 Yahoo! 效率手册 进行交换;Exchange 等)。

本文档描述事件库的概念及通用的编程任务。如果你想要从你的应用来显示或编辑日历事件和/或提醒数据,那么你应该阅读本文档。事件库提供了有限的对用户日历数据库的访问;但它并未包含期望实现完整特性的日历或提醒应用所需的每一个功能,诸如出席者和帐户。

Event Kit not only allows your app to retrieve users’ existing calendar and reminder data, but it also lets your app create new events and reminders for any of their calendars. In addition, Event Kit lets users edit and delete their events and reminders (collectively known as “calendar items”). More advanced tasks, such as adding alarms or specifying recurring events, can be achieved with Event Kit as well. If a change to the Calendar database occurs from outside of your app, Event Kit is able to detect the change by notification so your app can act appropriately. Changes made to calendar items with Event Kit are automatically synced to the associated calendar (CalDAV, Exchange, and so on).

This document describes Event Kit concepts and common programming tasks. You should read this document if you want to display or edit calendar events and/or reminder data from within your app. Event Kit provides limited access to a user’s Calendar database; it does not include everything that would be desired for implementing a full-featured calendar or reminder app, such as adding attendees or accounts.

    • 概览(At a Glance)

本文档包含如下几章,这些内容描述了如何在你的应用内集成用户的日历和提醒数据:

This document contains the following chapters, which describe how to integrate with users’ calendar and reminder data within your app:

  • “Reading and Writing Calendar Events” explains how to retrieve, create, and modify calendar events.
  • “Reading and Writing Reminders” explains how to retrieve, create, and modify reminders.
  • “Configuring Alarms” explains how to attach alarms to a calendar item.
  • “Creating Recurring Events” explains how to make an event a recurring event.
  • “Observing External Changes to the Calendar Database” explains how to register for notifications regarding external changes to the Calendar database.
  • “Providing Interfaces for Events” explains how to display event view controllers to allow your users to create and edit events.

    • 参见(See Also)

本文档是下面的示例代码和参考手册的配套指南:

  • SimpleEKDemo 是一个使用事件库中事件的示例代码工程。
  • Event Kit Framework Reference 提供了一个事件库 API 的较深入的描述,这些 API 就是一个访问用户的日历数据库的框架。
  • Event Kit UI Framework Reference 详细说明了事件库用户界面的 API,就是一个 iOS专有的框架,它提供了一些视图控制器用于显示和编辑日历事件。

This document is a companion guide to the following sample code and references:

  • SimpleEKDemo is an example sample code project that utilizes events in Event Kit.
  • Event Kit Framework Reference provides an in-depth description of the Event Kit API, a framework that grants access to a user’s Calendar database.
  • Event Kit UI Framework Reference details the Event Kit UI API, an iOS-specific framework that provides view controllers for displaying and editing calendar events.   
  • 读写日历事件(Reading and Writing Calendar Events )
你可以使用 EKEventStore 类从用户的日历数据库中获取、创建、编辑和删除事件。你可以获取匹配你提供的谓词的事件自定义的一组事件,或通过唯一标识获取一个单独的事件。你获取到一个事件后,可以使用 EKEvent 类的属性获取访问该事件相关的日历信息。同样的,你可以通过设置 EKEvent 类的属性来修改该事件的日历信息。

You can fetch, create, edit, and delete events from a user’s Calendar database using the EKEventStore class. You can fetch a custom set of events that match a predicate you provide, or you can fetch an individual event by its unique identifier. After you fetch an event, you can access its associated calendar information with the properties of the EKEvent class. Likewise, you can modify its calendar information by setting the properties of the EKEvent class.   

    • 连接到事件库(Connecting to the Event Store)    

在 iOS 5 及以后版本系统中,使用默认的初始化器:

On iOS 5 and later, initialize an EKEventStore object with the default initializer:

EKEventStore *store = [[EKEventStore alloc] init];

在 iOS 6 及以后版本,你必须在事件库初始化后,使用 requestAccessToEntityType:completion: 方法请求使用用户的日历数据库。请求访问某个实体类型会异步提示用户允许或禁止你的应用使用他们的日历信息。你应该处理用户授权或禁止你的应用访问权的各种状况:

On iOS 6 and later, you must request access to use the user’s Calendar database with the requestAccessToEntityType:completion: method after the event store is initialized. Requesting access to an entity type asynchronously prompts the user to allow or deny your app from using their calendar information. You should handle cases for when the user grants and denies access to your app:

[store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
    // handle access here
}];

On OS X and later, initialize an EKEventStore object with the designated initializer initWithAccessToEntityTypes::

EKEventStore *store = [[EKEventStore alloc] initWithAccessToEntityTypes:EKEntityMaskEvent];

You do not need to request access to use the user’s Calendar database on OS X, because access is automatically granted.

EKEventStore 对象需要相对较大量的时间来初始化和释放。因此,你不应该为每一个事件相关的任务都初始化和释放事件库。相反,在你的应用加载时,初始化一个事件库,然后反复地使用这一个来确保连接一直可用。

事件库实例不应该在其它事件开发包相对的对象释放前被释放,否则可能发生意想不到的状态。

An EKEventStore object requires a relatively large amount of time to initialize and release. Consequently, you should not initialize and release a separate event store for each event-related task. Instead, initialize a single event store when your app loads, and use it repeatedly to ensure that your connection is long-lived.

An event store instance must not be released before other Event Kit objects; otherwise, undefined behavior may occur.


    • 获取事件(Retrieving Events)

有两种方式获取事件。通过谓词或搜索查询获取,会返回零个或多个与给定查询匹配的事件。通过唯一标识获取会返回与给定标识相符的唯一的一个事件。

There are two ways to retrieve events. Fetching via predicates, or search query, will return zero or more events that match a given query. Fetching via unique identifiers will return a single event that corresponds to the given identifier.

注意:从日历数据库获取事件并不一定按时间顺序返回。要通过日期排序 EKEvent 对象的数组,可以在数组上调用 sortedArrayUsingSelector: 方法,并提供  compareStartDateWithEvent: 方法的选择器。

Note:Retrieving events from the Calendar database does not necessarily return events in chronological order. To sort an array of  EKEvent objects by date, call sortedArrayUsingSelector: on the array, providing the selector for the compareStartDateWithEvent: method.

      • 使用谓词(Using Predicates ) 

通常是要获得属于某一日期范围的事件。 EKEventStore 的 eventsMatchingPredicate: 方法获取属于你提供的谓词中指定的日期范围的所有事件。

It’s common to fetch events that fall within a date range. The EKEventStore method eventsMatchingPredicate: fetches all events that fall within the date range specified in the predicate you provide. 

列表 1-1 展示了如何获取发生在一天前和当前之后一年之内的所有事件。

Listing 1-1 demonstrates how to fetch all events that occur between one day before and one year after the current date.

注意:尽管  eventsMatchingPredicate: 方法接受一个 NSPredicate类型的参数,但你必须提供一个用 EKEventStore 的方法 predicateForEventsWithStartDate:endDate:calendars: 创建的谓词。

Note:Although the  eventsMatchingPredicate: method accepts a parameter of type NSPredicate, you must supply a predicate created with the EKEventStore method predicateForEventsWithStartDate:endDate:calendars:.

列表 1-1 使用谓词获取事件 Listing 1-1 Fetching events with a predicate

// 获取适当的日期(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];

你可以指定一个日历的子集来搜索,这需要传递一个 EKCalendar 对象的数组作为 predicateForEventsWithStartDate:endDate:calendars: 方法的 calendars 参数。你可以从事件库的 calendarsForEntityType: 方法获得用户的不同类型的日历。如果传一个 nil 值,那么就是告诉这个方法获取用户的所有日历。

You can specify a subset of calendars to search by passing an array of EKCalendar objects as the calendars parameter of the predicateForEventsWithStartDate:endDate:calendars: method. You can get the user’s calendars from the event store’s calendarsForEntityType: method. Passing nil tells the method to fetch from all of the user’s calendars.

因为方法  eventsMatchingPredicate: 是同步的,而你可能并不想在你的应用主线程中运行它。如果要异步执行的话,那么使用 dispatch_async 函数或使用一个 NSOperation 对象,就可以在另一个线程中运行该方法了。

Because the eventsMatchingPredicate: method is synchronous, you may not want to run it on your app’s main thread. For asynchronous behavior, run the method on another thread with the dispatch_async function or with an NSOperation object.

      • 使用唯一标识(Using Unique Identifiers )   

如果你之前使用谓词获得了一个事件并知道它的唯一标识,那么你可以使用 EKEventStore 的 eventWithIdentifier: 方法来再次获取该事件。如果它是一个循环事件,那么这个方法就会返回第一次出现的该事件。你可以使用属性 eventIdentifier 获得事件的唯一标识。

If you know the event’s unique identifier because you fetched it previously with a predicate, you can use the EKEventStore method eventWithIdentifier: to fetch the event. If it is a recurring event, this method will return the first occurrence of the event. You can get an event’s unique identifier with the eventIdentifier property.

    • 创建及编辑事件(Creating and Editing Events)    

注意:如果你正在 iOS上开发,那么你可以选择使用事件库用户界面框架提供的事件视图控制器来让用户修改事件数据。有关如何使用这些事件视图控制器的信息,参见“为事件提供界面。”

使用 事件 EKEvent eventWithEventStore: 方法创建一个新的事件。

你可以通过设置一个新的事件或先前从日历数据库获取的事件的对应属性来编辑事件。你可以编辑的详细内容包括:


Note: If you’re developing on iOS, you have the option of letting users modify event data with the event view controllers provided in the Event Kit UI framework. For information on how to use these event view controllers, see “Providing Interfaces for Events.”

Create a new event with the eventWithEventStore: method of the EKEvent class.

You can edit the details of a new event or an event you previously fetched from the Calendar database by setting the event’s corresponding properties. Some of the details you can edit include:

      • The event’s title with the title property

      • The event’s start and end dates with the startDate and endDate properties

      • The calendar with which the event is associated with the calendar property

      • The alarms associated with the event with the alarms property (see “Configuring Alarms” for more details)

      • The event’s recurrence rule, if it is a repeating event, with the recurrenceRules property (see “Creating Recurring Events” for more details)

    • 保存和移除事件(Saving and Removing Events)    

提示:如果你的应用修改用户的日历数据库,它必须在这之前先从用户获得确认。应用在未得到用户的特定指示的情况下决不可能修改日历数据库。

你对事件的修改不是持久化的,直到你保存它们为止。使用 EKEventStore 的 saveEvent:span:commit:error: 方法保存你的修改到日历数据库中。如果你要从日历数据库移除事件,使用 EKEventStore 的 removeEvent:span:commit:error: 方法。无论你保存或移除事件,各自实现的方法都会自动所做的修改到该事件所属于的日历(CalDav、Exchange等等)。

如果你保存一个循环事件,你可以通过给 saveEvent:span:commit:error: 方法的参数 span 指定 EKSpanFutureEvents 来使你的更改应用到所有未来出现的该事件中。同样地,你也可以指定 removeEvent:span:commit:error: 方法的 span 参数值为 EKSpanFutureEvents 来移除一个事件的所有未来的出现。

注意:如果你给 commit 参数传了 NO 值,那么要确保稍侯调用 commit: 方法以持久保存你的更改(译者注:默认传 YES 会立即持久保存更改)。

Important: If your app modifies a user’s Calendar database, it must get confirmation from the user before doing so. An app should never modify the Calendar database without specific instruction from the user.

Changes you make to an event are not permanent until you save them. Save your changes to the Calendar database with the EKEventStore methodsaveEvent:span:commit:error:. If you want to remove an event from the Calendar database, use the EKEventStore method removeEvent:span:commit:error:. Whether you are saving or removing an event, implementing the respective method automatically syncs your changes with the calendar the event belongs to (CalDAV, Exchange, and so on).

If you are saving a recurring event, your changes can apply to all future occurrences of the event by specifying EKSpanFutureEvents for the span parameter of the saveEvent:span:commit:error: method. Likewise, you can remove all future occurrences of an event by specifying EKSpanFutureEvents for the span parameter of theremoveEvent:span:commit:error: method.

Note: If you pass NO to the commit parameter, make sure that you later invoke the commit: method to permanently save your changes.

    • 执行批量事件操作(Performing Batch Operations on Events)    

你可以在 EKEventStore   enumerateEventsMatchingPredicate:usingBlock: 方法执行给定的谓词匹配的所有事件上执行同一个操作。你必须为上述方法使用 EKEventStore 的 predicateForEventsWithStartDate:endDate:calendars: 方法创建谓词。你提供的操作是 EKEventSearchCallback 类型的块。

You can perform an operation on all events that match a provided predicate with the EKEventStore method enumerateEventsMatchingPredicate:usingBlock:. You must create the predicate for this method with the EKEventStore method predicateForEventsWithStartDate:endDate:calendars:. The operation you provide is a block of typeEKEventSearchCallback.

typedef void (^EKEventSearchCallback)(EKEvent *event, BOOL *stop);

块接收两个参数The block is passed two parameters):

event

当前被操作的事件The event that is currently being operated on).

stop

一个布尔值,它决定当前块返回后 enumerateEventsMatchingPredicate:usingBlock: 方法是否应该停止继续处理事件。如果是 YES,那么与该谓词匹配的任何未处理的事件仍保持未处理状态。

提示:记住,使用该方法会引起对用户的日历数据库的有效的修改。确认在你向用户请求批准时,让用户清楚地知道你所要执行的操作。

A Boolean value determining whether enumerateEventsMatchingPredicate:usingBlock: should stop processing events when this block returns. If YES, any event that matches the predicate but has not yet been processed will remain unprocessed.

Important: Keep in mind that using this method can result in significant changes to the user’s Calendar database. Make sure the user is fully informed of the actions you are about to perform when you request user confirmation.


Because the enumerateEventsMatchingPredicate:usingBlock: method is synchronous, you may not want to run it on your app’s main thread. For asynchronous behavior, run the method on another thread with the dispatch_async function or with an NSOperation object.

  • 读写提醒(Reading and Writing Reminders)

提醒就是一些可以关联到特定时间或位置的任务。他们与日历事件很相似,但可以被标识为完成并且可以不必跨跃一段确切的时间。

因为 EKReminder 继承自 EKCalendarItem ,所以你可以在提醒上执行与在事件上一样的方法,诸如使用 addAlarm: 方法添加一个闹钟,或使用 addRecurrenceRule: 方法设置一个循环规则。

Reminders are tasks that may be tied to a specific time or location. They are similar to calendar events, but can be marked complete and may not necessarily span an exact period of time.

Because EKReminder inherits from EKCalendarItem, you can perform the same methods on a reminder as you would on an event, such as adding an alarm with addAlarm: or setting a recurrence rule with addRecurrenceRule:.

    • 获取提醒(Retrieving Reminders)    

和事件一样,你必须先建立与事件库的连接,才能访问已存在的提醒。如果你之前没做过事件库的连接操作,那么参见“连接到事件库”。

要在 OS X 上初始访问提醒的连接,传递的参数不是 EKEntityMaskEvent 而是 EKEntityMaskReminder 

As with events, you must first establish a connection to the event store to access existing reminders. See “Connecting to the Event Store” if you have not already done so.

To initialize a connection with access to reminders on OS X, pass EKEntityMaskReminder instead of EKEntityMaskEvent.

EKEventStore *store = [[EKEventStore alloc] initWithAccessToEntityTypes:EKEntityMaskReminder];

在 iOS 6 及以后的版本中,事件库初始化后,你必须使用 requestAccessToEntityType:completion: 请求对用户日历数据库的访问权。请求某一实体类型的访问权会提示用户允许或禁止你的应用使用日历信息。你应该处理用户授权或禁止访问每种情况:

On iOS 6 and later, you must request access to use the user’s Calendar database with the requestAccessToEntityType:completion: method after the event store is initialized. Requesting access to an entity type asynchronously prompts the user to allow or deny your app from using their calendar information. You should handle cases for when the user grants and denies access to your app:

[store requestAccessToEntityType:EKEntityTypeReminder completion:^(BOOL granted, NSError *error) {
    // handle access here
}];

在 OS X 上你不需要请求用户提醒的访问权,因为访问权是默认授予的(译者注:iOS 模拟器上也是默认授予的)。

正如搜索事件一样,也有两种途径来获取提醒。

You do not need to request access for the user’s reminders on OS X, because access is automatically granted.

Just like searching for events, there are two ways to retrieve reminders.

  • 使用谓词(Using Predicates)    

你可以调用 fetchRemindersMatchingPredicate:completion: 方法来访问与某一谓词匹配的多个提醒。传递的谓词可通过如下方法得到:


你可以给 completion 参数传一个块来迭代所有匹配的提醒,如列表 2-1 所示:

You can call fetchRemindersMatchingPredicate:completion: to access multiple reminders that match a predicate. Pass a predicate returned by one of the following methods:

      • predicateForIncompleteRemindersWithDueDateStarting:ending:calendars:

         finds incomplete reminders within an optional time period
      • predicateForCompletedRemindersWithCompletionDateStarting:ending:calendars:

         finds completed reminders within an optional time period
      • predicateForRemindersInCalendars:

         finds all reminders

You can iterate across matched reminders by passing a block to the completion argument, as shown in Listing 2-1.

列表 2-1 使用谓词获取提醒

Listing 2-1 Fetching reminders with a predicate

NSPredicate *predicate = [store predicateForRemindersInCalendars:nil];
 
[store fetchRemindersMatchingPredicate:predicate completion:^(NSArray *reminders) {
    for (EKReminder *reminder in reminders) {
        // do something for each reminder
    }
}];

注意:不像通过谓词获取事件那样(参见“使用谓词”),你可以通过谓词异步获取提醒,而无需分派到其它线程。

如果你想要中止通过谓词获取的请求,可以调用 cancelFetchRequest: 方法并传递之前由 fetchRemindersMatchingPredicate:completion: 方法返回的标识。

Note: Unlike fetching events via predicate (see “Using Predicates”), you can fetch reminders via predicate asynchronously without dispatching to another thread.

If you’d like to abort your fetch request by predicate, call cancelFetchRequest: while passing the identifier as returned by fetchRemindersMatchingPredicate:completion:.

  • 使用唯一标识(Using Unique Identifiers)  

如果你知道一个从先前用谓词获取到的提醒的唯一标识,那么你可以调用 calendarItemWithIdentifier: 实例方法,该方法可以获取到任何与该标识符匹配的日历项(提醒或事件),而 eventWithIdentifier: 方法只能获到事件。

If you know a specific reminder’s unique identifier from previously fetching it with a predicate, you can call the calendarItemWithIdentifier: instance method.calendarItemWithIdentifier: can fetch any calendar item (reminders and events), whereas eventWithIdentifier: fetches only events.

    • 创建及编辑提醒(Creating and Editing Reminders )  

You can create reminders using the reminderWithEventStore: class method. The title and calendar properties are required. The calendar for a reminder is the list with which it is grouped.

Like events, reminders can trigger time-based or location-based alarms to alert the user of a certain task. Read “Configuring Alarms” for more information on how to attach alarms to calendar items.

To associate a start date or due date with a reminder, use the startDateComponents and dueDateComponents properties. To complete a reminder, set the completed property to YES, which automatically sets completionDate to the current date.

    • 保存和移除提醒(Saving and Removing Reminders)   

Important: If your app modifies a user’s Calendar database, it must get confirmation from the user before doing so. An app should never modify the Calendar database without specific instruction from the user.

Reminders are saved in a similar fashion to events. To save a reminder to the Calendar database, call the saveReminder:commit:error: method. To remove an event, call the removeReminder:commit:error: method.

Remember, the title and calendar properties must explicitly be set before you save your reminder.

Note: Just like when saving or removing events, make sure that if you pass NO to the commit parameter, you later invoke the commit: method to save your changes.

  • 配置闹钟(Configuring Alarms)    

An easy way to alert users of their upcoming events is to give them the option of setting alarms for their calendar items. Regardless of the app that’s currently running, alarms come to the foreground as a notification and remind users of the scheduled event. If an alarm is set to a calendar event, the notification comes from the Calendar app; if an alarm is set to a reminder, the notification comes from the Reminders app. Alarms can be time-based, firing at a specified time, or location-based, firing when crossing a geofence (for more information about geofences, see “Setting Geofences”).

Alarms can be applied to both calendar events and reminders.

Note: An alarm is not intended to serve as a UILocalNotification. An alarm requires you to create an event or reminder that is visible in the user’s Calendar or Reminders app. A UILocalNotification is better suited for general purposes that don’t involve the Calendar database.
    • 附加和移除闹钟(Attaching and Removing Alarms) 

You can add an alarm to an event with the addAlarm: method. Alarms can be created with an absolute date or with an offset relative to the start date of the event. Alarms created with a relative offset must occur before or at the start date of the event.

In OS X, you can trigger an action alongside the alarm; for example, set:

      • The emailAddress property to send an email

      • The soundName property to play a sound

      • The url property to open a URL

You can remove an alarm from an event with the removeAlarm: method.

    • 设置地理围栏(Setting Geofences)    

Note: Geofences are supported on both OS X and iOS, but they are more effective on mobile devices.

A geofence is a virtual border surrounding a geographic location that, when crossed, can trigger an alarm for an event. Geofences are a useful way to remind users of tasks they need to do when entering or exiting a certain region. For example, when a user leaves his workplace, an alarm can fire that reminds him to stop by the grocery store. As a developer, you have control over specifying the latitude and longitude of the center, as well as the radius of the geofence.

Configure a geofence for an event by creating an alarm and setting its structured location and proximity. Call the locationWithTitle: method to create a structured location. To set longitude and latitude coordinates, pass a CLLocation to the geoLocation property of the structured location returned. A value of 0 for the radius property will use the system’s default radius; to choose a radius of your own, specify a value in meters.

While geofence-enabled alarms can be applied to events, they are more practical for reminders.

  • 创建循环事件(Creating Recurring Events) 

Recurring events repeat over a specified interval of time. To make an event a recurring event, assign it a recurrence rule, which describes when the event occurs. Recurrence rules are represented by instances of the EKRecurrenceRule class.

Recurrence is applicable to both calendar events and reminders. Unlike with recurring events, only the first incomplete reminder of a recurring set is obtainable. This is true with Event Kit as well as the Reminders app. When the reminder is completed, the next reminder in the recurrence set becomes available.

    • 使用基本规则(Using Basic Rules) 

You can create a recurrence rule with a simple daily, weekly, monthly, or yearly pattern using the initRecurrenceWithFrequency:interval:end: method. You provide three values to this method:

      • The recurrence frequency

        . This is a value of type EKRecurrenceFrequency that indicates whether the recurrence rule is daily, weekly, monthly, or yearly.
      • The recurrence interval

        . This is an integer greater than 0 that specifies how often a pattern repeats. For example, if the recurrence rule is a weekly recurrence rule and its interval is 1, then the pattern repeats every week. If the recurrence rule is a monthly recurrence rule and its interval is 3, then the pattern repeats every three months.
      • The recurrence end

        . This optional parameter is an instance of the EKRecurrenceEnd class, which indicates when the recurrence rule ends. The recurrence end can be based on a specific end date or on an amount of occurrences.
If you don’t want to specify an end for the recurrence rule, pass nil.
    • 使用复杂的规则(Using Complex Rules)  

You can create a recurrence rule with a complex pattern using the 

initRecurrenceWithFrequency:interval:daysOfTheWeek:daysOfTheMonth:monthsOfTheYear:weeksOfTheYear:daysOfTheYear:setPositions:end: 

method. As you do for a basic recurrence rule, you provide a frequency, an interval, and an optional end for the recurring event. In addition, you can provide a combination of optional values describing a custom rule, as listed in Table 4-1.

Table 4-1  Complex recurrence rule breakdown

Parameter name

Accepted values

Can be combined with

Example

days

The days of the week on which the event occurs.

An array ofEKRecurrenceDayOfWeek objects.

All recurrence rules except for daily recurrence rules.

An array containing EKTuesday and EKFriday objects will create a recurrence that occurs every Tuesday and Friday.

monthDays

The days of the month on which the event occurs.

An array of nonzero NSNumberobjects ranging from –31 to 31. Negative values indicate counting backward from the end of the month.

Monthly recurrence rules only.

An array containing the values 1 and –1 will create a recurrence that occurs on the first and last day of every month.

months

The months of the year in which the event occurs.

An array of NSNumber objects with values ranging from 1 to 12, corresponding to Gregorian calendar months.

Yearly recurrence rules only.

If your originating event occurs on January 10, you can provide an array containing the values 1 and 2 to create a recurrence that occurs every January 10 and February 10.

weeksOfTheYear

The weeks of the year in which the event occurs.

An array of nonzero NSNumberobjects ranging from –53 to 53. Negative values indicate counting backward from the end of the year.

Yearly recurrence rules only.

If your originating event occurs on a Wednesday, you can provide an array containing the values 1 and –1 to create a recurrence that occurs on the Wednesday of the first and last weeks of every year. If a specified week does not contain a Wednesday in the current year, as can be the case for the first or last week of a year, the event does not occur.

daysOfTheYear

The days of the year on which the event occurs.

An array of nonzero NSNumberobjects ranging from –366 to 366. Negative values indicate counting backward from the end of the year.

Yearly recurrence rules only.

You can provide an array containing the values 1 and –1 to create a recurrence that occurs on the first and last day of every year.

setPositions

The occurrences to include in the recurrence rule. This filter is applied to the set of occurrences determined from the other parameters you provide.

An array of nonzero NSNumberobjects ranging from –366 to 366. Negative values indicate counting backward from the end of the list of occurrences.

All recurrence rules except for daily recurrence rules.

If you provide an array containing the values 1 and –1 to a yearly recurrence rule that has specified Monday through Friday as its value for days of the week, the recurrence occurs only on the first and last weekday of every year.

You can provide values for any number of the parameters in Table 4-1. Parameters that don’t apply to a particular recurrence rule are ignored. 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.

Once you have created a recurrence rule, you can apply it to a calendar event or reminder with the addRecurrenceRule: instance method of EKCalendarItem.

  • 监视日历数据库的外部更改(Observing External Changes to the Calendar Database) 

It’s possible for another process or app to modify the Calendar database while your app is running. If your app fetches calendar events or reminders, you should register to be notified about changes to the Calendar database. By doing so, you ensure that the calendar and reminder information you display to the user is current.

    • 注册通知(Registering for Notifications)

An EKEventStore object posts an EKEventStoreChangedNotification notification whenever it detects changes to the Calendar database. Register for this notification if your app handles event or reminder data.

The following code registers for the EKEventStoreChangedNotification notification, as shown in Listing 5-1.

Listing 5-1 The EKEventStoreChangedNotification notification

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(storeChanged:)
                                             name:EKEventStoreChangedNotification
                                           object:eventStore];
    • 响应通知(Responding to Notifications)    

When you receive an EKEventStoreChangedNotification notification, it’s possible that objects you’ve fetched—such as an EKEventEKReminder, or EKCalendar, among others—have changed. The effect of these changes depends on whether an event was added, modified, or deleted.

      • If an event was added, it does not affect any of your previously fetched events or reminders, but the added event may fall within the date range of events you are displaying to the user.

      • If an event was modified or deleted, properties of EKEvent and EKReminder objects representing that event may become out of date.

Because your local data is often invalidated or incomplete when a change occurs in the Calendar database, you should refetch your current date range of events whenever you receive an EKEventStoreChangedNotification notification. If you are currently modifying an event and you do not want to refetch it unless it is absolutely necessary to do so, you can call the refresh method on the event. If the method returns YES, you can continue to use the event; otherwise, you need to refetch it.

Note: Events being modified in an event view controller with Event Kit UI for iOS are updated automatically when a change occurs in the Calendar database. For a more in-depth look at Event Kit UI, read the next chapter, “Providing Interfaces for Events.”
  • 提供事件界面(Providing Interfaces for Events)  
Important: The Event Kit UI framework, as referenced by this chapter, is available for iOS only. If you are developing for OS X, you are responsible for constructing your own event view controllers; for more information, read NSViewController Class Reference.

The Event Kit UI framework provides two types of view controllers for manipulating events:

    • 显示事件数据(Displaying Event Data) 

To use the EKEventViewController class, you must have an existing event you obtained from an event store. You need to set the event property and any other display options before presenting this type of view controller. Listing 6-1 shows how to create an event view controller and add it to a navigation controller, assuming myEvent already exists. If you don’t allow the user to edit the event, set the allowsEditing property to NO.

Listing 6-1 Editing an existing event

EKEventViewController *eventViewController = [[EKEventViewController alloc] init];
eventViewController.event = myEvent;
eventViewController.allowsEditing = YES;
navigationController = [[UINavigationController alloc] initWithRootViewController:eventViewController];

You need to assign a delegate to an event view controller to receive a notification when the user finishes viewing the event. The delegate conforms to the EKEventViewDelegateprotocol and must implement the eventViewController:didCompleteWithAction: method.

    • 修改事件数据(Modifying Event Data)  

To allow the user to create, edit, or delete events, use the EKEventEditViewController class and the EKEventEditViewDelegate protocol. You create an event edit view controller similar to an event view controller, except you must set the eventStore property (setting the event property is optional).

      • If the event property is nil when you present the view controller, the user creates a new event in the default calendar and saves it to the specified event store.

      • If the event property is not nil, the user edits an existing event. The event must reside in the specified event store—otherwise, an exception is raised.

Instances of the EKEventEditViewController class are designed to be presented modally, as shown in Listing 6-2. In this code fragment, self is the top view controller of a navigation controller. For details on modal view controllers, read “Presenting a View Controller Modally” in View Controller Programming Guide for iOS.

Listing 6-2 Presenting an event edit view controller modally

EKEventEditViewController* controller = [[EKEventEditViewController alloc] init];
controller.eventStore = myEventStore;
controller.editViewDelegate = self;
[self presentModalViewController:controller animated:YES];

You must also specify a delegate to receive notification when the user finishes editing the event. The delegate conforms to the EKEventEditViewDelegate protocol and must implement the eventEditViewController:didCompleteWithAction: method to dismiss the modal view controller, as shown in Listing 6-3. In general, the object that presents a view controller modally is responsible for dismissing it.

Listing 6-3 The delegate dismisses the modal view

- (void)eventEditViewController:(EKEventEditViewController *)controller
          didCompleteWithAction:(EKEventEditViewAction)action
{
    [self dismissModalViewControllerAnimated:YES];
}

The delegate is also passed the action the user took when finishing the edit. The user can either cancel the changes, save the event, or delete the event. If you need to execute more code after the user dismisses the modal view, implement the eventEditViewController:didCompleteWithAction: delegate method.

  • 文档修订历史(Document Revision History)   

This table describes the changes to Calendar and Reminders Programming Guide.

Date Notes
2012-07-17 Updated to support OS X Mountain Lion.
2010-09-22 Made improvements throughout.
2010-08-03 Added a link to the SimpleEKDemo sample code.
2010-04-29 New document that explains how to access calendar data in iOS with the Event Kit framework.






上一篇:《Adobe Flash CS5中文版经典教程》——1.12 查找关于使用Flash的资源


下一篇:自动化运维平台之系统自动化安装Cobbler系统使用详解