[iOS]日历和提醒编程指南(Calendar and Reminders Programming Guide)
太阳火神的美丽人生 (http://blog.csdn.net/opengl_es)
本文遵循“署名-非商业用途-保持一致”创作公用协议
转载请保留此句:太阳火神的美丽人生 - 本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。
事件库框架授权访问用户的
Calendar.app
和Reminders.app 应用的
信息。尽管是用两个不同的应用显示用户的日历和提醒数据,但确是同一个框架维护这份数据。同样地,存储这份数据的数据库叫做日历数据库,同时容纳日历和提醒信息。The Event Kit framework grants access to users’
Calendar.app
andReminders.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
事件库不但允许你的应用获取用户已经存在的日历及提醒数据,而且它可以让你的应用为任何日历创建新的事件和提醒。另外,事件库让用户可以编辑和删除他们的事件和提醒(整体叫做“日历项”)。更高级的任务,诸如添加闹钟或指定循环事件,也可以使用事件库完成。如果日历数据库有来自你的应用外部的更改发生,事件库可以通过通知监测到,这样你的应用可以做出适当的响应。使用事件库对日历项所做的更改会自动地同步到相关的日历(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.
本文档包含如下几章,这些内容描述了如何在你的应用内集成用户的日历和提醒数据:
- “读写日历事件” 描述了如何获取、创建和修改日历事件。
- “读写提醒” 描述了如何获取、创建和修改提醒。
- “配置闹钟” 描述了如何附加闹钟到一个日历项。
- “创建循环的事件” 描述了如何使一个事件变成循环事件。
- “监视日历数据库的外部更改” 描述了如何注册通知以关注日历数据库的外部更改。
- “提供事件界面” 描述了如何显示事件视图控制器以允许你的用户来创建和编辑事件。
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.
本文档是下面的示例代码和参考手册的配套指南:
- 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.
你可以使用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 theEKEvent
class. Likewise, you can modify its calendar information by setting the properties of theEKEvent
class.
在 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 initializerinitWithAccessToEntityTypes:
:
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.
有两种方式获取事件。通过谓词或搜索查询获取,会返回零个或多个与给定查询匹配的事件。通过唯一标识获取会返回与给定标识相符的唯一的一个事件。
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, callsortedArrayUsingSelector:
on the array, providing the selector for thecompareStartDateWithEvent:
method.
通常是要获得属于某一日期范围的事件。
EKEventStore 的
eventsMatchingPredicate:
方法获取属于你提供的谓词中指定的日期范围的所有事件。It’s common to fetch events that fall within a date range. The
EKEventStore
methodeventsMatchingPredicate:
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 typeNSPredicate
, you must supply a predicate created with theEKEventStore
methodpredicateForEventsWithStartDate:endDate:calendars:
.
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 thepredicateForEventsWithStartDate:endDate:calendars:
method. You can get the user’s calendars from the event store’scalendarsForEntityType:
method. Passingnil
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 thedispatch_async
function or with anNSOperation
object.
如果你之前使用谓词获得了一个事件并知道它的唯一标识,那么你可以使用
EKEventStore
的eventWithIdentifier:
方法来再次获取该事件。如果它是一个循环事件,那么这个方法就会返回第一次出现的该事件。你可以使用属性eventIdentifier
获得事件的唯一标识。
If you know the event’s unique identifier because you fetched it previously with a predicate, you can use the
EKEventStore
methodeventWithIdentifier:
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 theeventIdentifier
property.
注意:如果你正在 iOS上开发,那么你可以选择使用事件库用户界面框架提供的事件视图控制器来让用户修改事件数据。有关如何使用这些事件视图控制器的信息,参见“为事件提供界面。”
使用 事件
EKEvent
的eventWithEventStore: 方法创建一个新的事件。你可以通过设置一个新的事件或先前从日历数据库获取的事件的对应属性来编辑事件。你可以编辑的详细内容包括:
事件的标题用
title
属性事件的起始日期用
startDate
和endDate
属性与事件关联的日历用属性
calendar
与事件相关的闹钟用
alarms
属性 (参见 “配置闹钟” 以获得更多详细信息)如果一个事件是循环事件,那么它的循环规则用属性
recurrenceRules
(参见 “创建循环事件” 以获得更多详细信息)
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 theEKEvent
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)
提示:如果你的应用修改用户的日历数据库,它必须在这之前先从用户获得确认。应用在未得到用户的特定指示的情况下决不可能修改日历数据库。
你对事件的修改不是持久化的,直到你保存它们为止。使用
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 theEKEventStore
methodremoveEvent: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 thesaveEvent:span:commit:error:
method. Likewise, you can remove all future occurrences of an event by specifyingEKSpanFutureEvents
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 thecommit:
method to permanently save your changes.
你可以在
EKEventStore
的enumerateEventsMatchingPredicate:usingBlock:
方法执行给定的谓词
匹配的所有事件上执行同一个操作。你必须为上述方法使用EKEventStore
的predicateForEventsWithStartDate:endDate:calendars:
方法创建谓词。你提供的操作是 EKEventSearchCallback 类型的块。
You can perform an operation on all events that match a provided predicate with the
EKEventStore
methodenumerateEventsMatchingPredicate:usingBlock:
. You must create the predicate for this method with theEKEventStore
methodpredicateForEventsWithStartDate: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. IfYES
, 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 thedispatch_async
function or with anNSOperation
object.
提醒就是一些可以关联到特定时间或位置的任务。他们与日历事件很相似,但可以被标识为完成并且可以不必跨跃一段确切的时间。
因为
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 fromEKCalendarItem
, you can perform the same methods on a reminder as you would on an event, such as adding an alarm withaddAlarm:
or setting a recurrence rule withaddRecurrenceRule:
.
和事件一样,你必须先建立与事件库的连接,才能访问已存在的提醒。如果你之前没做过事件库的连接操作,那么参见“连接到事件库”。
要在 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 ofEKEntityMaskEvent
.
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:
方法来访问与某一谓词匹配的多个提醒。传递的谓词可通过如下方法得到:
找到(指定的日历中)在某一选定的时间段内所有未完成的提醒
predicateForIncompleteRemindersWithDueDateStarting:ending:calendars:
找到(指定的日历中)某一选定的时间段内所有完成的提醒
predicateForCompletedRemindersWithCompletionDateStarting:ending:calendars:
找到(指定的日历中)所有提醒
predicateForRemindersInCalendars:
你可以给 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:
finds incomplete reminders within an optional time period
predicateForIncompleteRemindersWithDueDateStarting:ending:calendars:
finds completed reminders within an optional time period
predicateForCompletedRemindersWithCompletionDateStarting:ending:calendars:
finds all reminders
predicateForRemindersInCalendars:
You can iterate across matched reminders by passing a block to the completion argument, as shown in 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 byfetchRemindersMatchingPredicate: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), whereaseventWithIdentifier:
fetches only events.
You can create reminders using the
reminderWithEventStore:
class method. Thetitle
andcalendar
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
anddueDateComponents
properties. To complete a reminder, set thecompleted
property toYES
, which automatically setscompletionDate
to the current date.
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 theremoveReminder:commit:error:
method.
Remember, the
title
andcalendar
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 thecommit:
method to save your changes.
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 aUILocalNotification
. An alarm requires you to create an event or reminder that is visible in the user’s Calendar or Reminders app. AUILocalNotification
is better suited for general purposes that don’t involve the Calendar database.
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.
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 aCLLocation
to thegeoLocation
property of the structured location returned. A value of0
for theradius
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.
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.
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 typeEKRecurrenceFrequency
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 is1
, 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 theEKRecurrenceEnd
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, passnil
.
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.
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 of
EKRecurrenceDayOfWeek
objects.All recurrence rules except for daily recurrence rules.
An array containing
EKTuesday
andEKFriday
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
NSNumber
objects 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
and2
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
NSNumber
objects 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
NSNumber
objects 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
NSNumber
objects 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 ofEKCalendarItem
.
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.
An
EKEventStore
object posts anEKEventStoreChangedNotification
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.The
EKEventStoreChangedNotification
notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(storeChanged:) name:EKEventStoreChangedNotification object:eventStore];
When you receive an
EKEventStoreChangedNotification
notification, it’s possible that objects you’ve fetched—such as anEKEvent
,EKReminder
, orEKCalendar
, 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 therefresh
method on the event. If the method returnsYES
, 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.”
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:
EKEventViewController
EKEventEditViewController
To use the
EKEventViewController
class, you must have an existing event you obtained from an event store. You need to set theevent
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, assumingmyEvent
already exists. If you don’t allow the user to edit the event, set theallowsEditing
property toNO
.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
EKEventViewDelegate
protocol and must implement theeventViewController:didCompleteWithAction:
method.
To allow the user to create, edit, or delete events, use the
EKEventEditViewController
class and theEKEventEditViewDelegate
protocol. You create an event edit view controller similar to an event view controller, except you must set theeventStore
property (setting theevent
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.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 theeventEditViewController: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.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.
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.