Xamarin.iOS 中的 EventKit

iOS 內建了兩個行事曆相關應用程式:行事曆應用程式,以及 Reminders 應用程式。 只要瞭解行事曆應用程式如何管理行事曆數據,就夠簡單了,但 Reminders 應用程式較不明顯。 提醒實際上可以有與其相關聯的日期,就到期時間、完成時間等而言。因此,iOS 會將所有行事曆數據儲存在稱為 行事曆資料庫的一個位置,無論是行事曆事件還是提醒。

EventKit 架構可讓您存取行事曆資料庫所儲存的 行事曆行事曆事件提醒 數據。 自 iOS 4 以來,已有行事曆和行事曆事件的存取權,但 iOS 6 中的提醒存取是新的。

在本指南中,我們將討論:

  • EventKit 基本概念 – 這會透過主要類別介紹 EventKit 的基本部分,並提供其使用方式的瞭解。 在處理檔的下一個部分之前,需要閱讀本節。
  • 一般工作 – 一般工作 區段旨在快速參考如何執行常見作業,例如:列舉行事曆、建立、儲存和擷取行事歷事件和提醒,以及使用內建控制器來建立和修改行事歷事件。 本節不需要從前面到后讀取,因為其應該是特定工作的參考。

本指南中的所有工作都可在隨附範例應用程式中取得:

隨附範例應用程式畫面

需求

EventKit 是在 iOS 4.0 中引進的,但 iOS 6.0 中引進了 Reminders 數據的存取權。 因此,若要進行一般 EventKit 開發,您必須以至少 4.0 版和 6.0 版作為提醒。

此外,模擬器上無法使用 Reminders 應用程式,這表示除非您先新增提醒數據,否則也不會提供提醒數據。 此外,存取要求只會對實際裝置上的用戶顯示。 因此,EventKit 開發在裝置上已經過最佳測試。

事件套件基本概念

使用 EventKit 時,請務必掌握一般類別及其使用方式。 所有這些類別都可以在和 EventKitUI 中找到EventKit(針對EKEventEditController)。

EventStore

EventStore 類別是 EventKit 中最重要的類別,因為必須在 EventKit 中執行任何作業。 它可以視為所有 EventKit 數據的持續性記憶體或資料庫引擎。 您可以 EventStore 存取行事曆應用程式中的行事曆和行事曆事件,以及提醒應用程式中的提醒。

因為 EventStore 就像資料庫引擎一樣,所以它應該長期存留,這表示應該在應用程式實例的存留期內盡可能少地建立和終結。 事實上,建議您在應用程式中建立一個 實例 EventStore 之後,請保留整個應用程式存留期的參考,除非您確定不會再用到它。 此外,所有呼叫都應該移至單 EventStore 一實例。 基於這個理由,建議使用Singleton模式來保留單一實例。

建立事件存放區

下列程式代碼說明建立 類別單一實例的有效 EventStore 方式,並從應用程式內以靜態方式提供它:

public class App
{
    public static App Current {
            get { return current; }
    }
    private static App current;

    public EKEventStore EventStore {
            get { return eventStore; }
    }
    protected EKEventStore eventStore;

    static App ()
    {
            current = new App();
    }
    protected App () 
    {
            eventStore = new EKEventStore ( );
    }
}

上述程式代碼會使用Singleton模式,在應用程式載入時具現化的 EventStore 實例。 EventStore然後,可以從應用程式內全域存取 ,如下所示:

App.Current.EventStore;

請注意,此處的所有範例都會使用此模式,因此會透過 App.Current.EventStore參考 EventStore

要求存取行事曆和提醒數據

在允許透過 EventStore 存取任何資料之前,應用程式必須先要求存取行事曆事件資料或提醒數據,視您需要的數據而定。 為了方便這一點,會 EventStore 公開稱為 RequestAccess 的方法,呼叫時會向使用者顯示警示檢視,告知使用者應用程式要求存取行事歷數據,或提醒數據,視 EKEntityType 傳遞給它的方式而定。 因為它會引發警示檢視,所以呼叫是異步的,而且會呼叫傳遞為 NSAction (或 Lambda) 的完成處理程式,以接收兩個 NSError參數;是否授與存取權的布爾值,以及 ,如果不是 Null,則會在要求中包含任何錯誤資訊。 例如,下列自動程式化會要求存取行事曆事件數據,並在未授與要求時顯示警示檢視。

App.Current.EventStore.RequestAccess (EKEntityType.Event, 
    (bool granted, NSError e) => {
            if (granted)
                    //do something here
            else
                    new UIAlertView ( "Access Denied", 
"User Denied Access to Calendar Data", null,
"ok", null).Show ();
            } );

一旦授與要求之後,只要應用程式安裝在裝置上,就不會對用戶發出警示,就會記住此要求。 不過,只有授與行事曆事件或已授與提醒的資源類型存取權。 如果應用程式需要兩者的存取權,則應該同時要求兩者。

因為會記住許可權,所以每次提出要求會比較便宜,所以最好先一律要求存取權,再執行作業。

此外,由於完成處理程式是在個別的 (非 UI) 線程上呼叫,因此應該透過 InvokeOnMainThread呼叫完成處理程式中 UI 的任何更新,否則會擲回例外狀況,如果未攔截,應用程式將會當機。

EKEntityType

EKEntityType 是描述專案或數據類型的 EventKit 列舉。 它有兩個值: Event 和 Reminder。 它用於許多方法,包括 EventStore.RequestAccess 告訴 EventKit 要取得或擷取何種類型的數據。

EKCalendar

EKCalendar 代表行事曆,其中包含一組行事曆事件。 行事歷可以儲存在許多不同的位置,例如本機、 iCloud、第三方提供者位置,例如 Exchange ServerGoogle 等。多次 EKCalendar 用來告知 EventKit 要尋找事件的位置,或儲存事件的位置。

EKEventEditController

您可以在 命名空間中找到 EventKitUI EKEventEditController,而且是內建控制器,可用來編輯或建立行事曆事件。 就像內建相機控制器一樣, EKEventEditController 在顯示UI和處理儲存方面會為您執行繁重的工作。

EKEvent

EKEvent 代表行事曆事件。 EKEvent和 都EKReminder繼承自 EKCalendarItem ,並具有 、TitleNotes、 等欄位。

EKReminder

EKReminder 代表提醒專案。

EKSpan

EKSpan 是一個列舉,描述修改可遞歸的事件時的事件範圍,並具有兩個值: ThisEventFutureEventsThisEvent 表示任何變更只會發生在所參考之數列中的特定事件,而 FutureEvents 會影響該事件和所有未來的週期。

工作

為了方便使用,EventKit 使用方式已分成一般工作,如下列各節所述。

列舉行事曆

若要列舉使用者在裝置上設定的行事曆,請在 上EventStore呼叫 GetCalendars ,並傳遞您想要接收的行事曆類型(提醒或事件):

EKCalendar[] calendars = 
App.Current.EventStore.GetCalendars ( EKEntityType.Event );

使用內建控制器新增或修改事件

如果您想要使用行事曆應用程式時,以相同的 UI 建立或編輯事件,EKEventEditViewController 會為您執行許多繁重的工作:

使用行事曆應用程式時向用戶顯示的UI

若要使用它,您會想要將它宣告為類別層級變數,以便在方法內宣告時不會進行垃圾收集:

public class HomeController : DialogViewController
{
        protected CreateEventEditViewDelegate eventControllerDelegate;
        ...
}

然後,若要啟動它:具現化它、為它提供參考 EventStore、連接 EKEventEditViewDelegate 委派給它,然後使用 加以顯示 PresentViewController

EventKitUI.EKEventEditViewController eventController = 
        new EventKitUI.EKEventEditViewController ();

// set the controller's event store - it needs to know where/how to save the event
eventController.EventStore = App.Current.EventStore;

// wire up a delegate to handle events from the controller
eventControllerDelegate = new CreateEventEditViewDelegate ( eventController );
eventController.EditViewDelegate = eventControllerDelegate;

// show the event controller
PresentViewController ( eventController, true, null );

或者,如果您想要預先填入事件,您可以建立全新的事件(如下所示),也可以擷取已儲存的事件:

EKEvent newEvent = EKEvent.FromStore ( App.Current.EventStore );
// set the alarm for 10 minutes from now
newEvent.AddAlarm ( EKAlarm.FromDate ( DateTime.Now.AddMinutes ( 10 ) ) );
// make the event start 20 minutes from now and last 30 minutes
newEvent.StartDate = DateTime.Now.AddMinutes ( 20 );
newEvent.EndDate = DateTime.Now.AddMinutes ( 50 );
newEvent.Title = "Get outside and exercise!";
newEvent.Notes = "This is your reminder to go and exercise for 30 minutes.”;

如果您要預先填入 UI,請務必在控制器上設定 Event 屬性:

eventController.Event = newEvent;

若要使用現有的事件,請參閱 稍後的依標識符 擷取事件一節。

委派應該覆寫 Completed 方法,當使用者完成對話框時,控制器會呼叫此方法:

protected class CreateEventEditViewDelegate : EventKitUI.EKEventEditViewDelegate
{
        // we need to keep a reference to the controller so we can dismiss it
        protected EventKitUI.EKEventEditViewController eventController;

        public CreateEventEditViewDelegate (EventKitUI.EKEventEditViewController eventController)
        {
                // save our controller reference
                this.eventController = eventController;
        }

        // completed is called when a user eith
        public override void Completed (EventKitUI.EKEventEditViewController controller, EKEventEditViewAction action)
        {
                eventController.DismissViewController (true, null);
                }
        }
}

或者,在委派中,您可以檢查 方法中的 Completed Action 以修改事件並重新儲存,或執行其他動作,如果取消,etcetera:

public override void Completed (EventKitUI.EKEventEditViewController controller, EKEventEditViewAction action)
{
        eventController.DismissViewController (true, null);

        switch ( action ) {

        case EKEventEditViewAction.Canceled:
                break;
        case EKEventEditViewAction.Deleted:
                break;
        case EKEventEditViewAction.Saved:
                // if you wanted to modify the event you could do so here,
// and then save:
                //App.Current.EventStore.SaveEvent ( controller.Event, )
                break;
        }
}

以程序設計方式建立事件

若要在程式代碼中建立事件,請在 類別上使用 EKEvent FromStore Factory方法,並在其中設定任何數據:

EKEvent newEvent = EKEvent.FromStore ( App.Current.EventStore );
// set the alarm for 10 minutes from now
newEvent.AddAlarm ( EKAlarm.FromDate ( DateTime.Now.AddMinutes ( 10 ) ) );
// make the event start 20 minutes from now and last 30 minutes
newEvent.StartDate = DateTime.Now.AddMinutes ( 20 );
newEvent.EndDate = DateTime.Now.AddMinutes ( 50 );
newEvent.Title = "Get outside and do some exercise!";
newEvent.Notes = "This is your motivational event to go and do 30 minutes of exercise. Super important. Do this.";

您必須設定您想要儲存事件的行事曆,但如果您沒有喜好設定,您可以使用預設值:

newEvent.Calendar = App.Current.EventStore.DefaultCalendarForNewEvents;

若要儲存事件,請在 EventStore呼叫 SaveEvent 方法:

NSError e;
App.Current.EventStore.SaveEvent ( newEvent, EKSpan.ThisEvent, out e );

儲存之後, EventIdentifier 屬性將會更新為唯一標識碼,以供稍後用來擷取事件:

Console.WriteLine ("Event Saved, ID: " + newEvent.CalendarItemIdentifier);

EventIdentifier 是字串格式化的 GUID。

以程序設計方式建立提醒

在程式代碼中建立提醒與建立行事曆事件大相等:

EKReminder reminder = EKReminder.Create ( App.Current.EventStore );
reminder.Title = "Do something awesome!";
reminder.Calendar = App.Current.EventStore.DefaultCalendarForNewReminders;

若要儲存,請在 EventStore呼叫 SaveReminder 方法:

NSError e;
App.Current.EventStore.SaveReminder ( reminder, true, out e );

依標識符擷取事件

若要依其標識符擷取事件,請使用 上的 EventFromIdentifier 方法,並將從事件提取的 傳遞給它EventIdentifierEventStore

EKEvent mySavedEvent = App.Current.EventStore.EventFromIdentifier ( newEvent.EventIdentifier );

針對事件,還有其他兩個標識符屬性,但 EventIdentifier 是唯一適用於此屬性的屬性。

依標識符擷取提醒

若要擷取提醒,請使用 上的 EventStore GetCalendarItem 方法,並將 CalendarItemIdentifier 傳遞給它

EKCalendarItem myReminder = App.Current.EventStore.GetCalendarItem ( reminder.CalendarItemIdentifier );

因為 GetCalendarItem 會傳EKCalendarItem回 ,所以如果您需要存取鬧鐘資料,或是稍後使用 實體,EKReminder則必須轉換成 EKReminder

請勿用於 GetCalendarItem 行事曆活動,如同在撰寫時一樣,它無法運作。

刪除事件

若要刪除行事曆事件,請在您的 上呼叫 RemoveEvent,並傳遞事件的參考,以及適當的 EKSpanEventStore

NSError e;
App.Current.EventStore.RemoveEvent ( mySavedEvent, EKSpan.ThisEvent, true, out e);

不過請注意,刪除事件之後,事件參考將會是 null

刪除提醒

若要刪除提醒,請在 上EventStore呼叫 RemoveReminder,並傳遞提醒的參考:

NSError e;
App.Current.EventStore.RemoveReminder ( myReminder as EKReminder, true, out e);

請注意,在上述程式代碼中,轉換成 EKReminder,因為 GetCalendarItem 是用來擷取它

搜尋事件

若要搜尋行事曆事件,您必須透過 上的 EventStorePredicateForEvents 方法建立 NSPredicate 物件。 NSPredicate是 iOS 用來尋找相符項目的查詢資料物件:

DateTime startDate = DateTime.Now.AddDays ( -7 );
DateTime endDate = DateTime.Now;
// the third parameter is calendars we want to look in, to use all calendars, we pass null
NSPredicate query = App.Current.EventStore.PredicateForEvents ( startDate, endDate, null );

建立 之後,NSPredicate請使用 上的 EventStoreEventsMatching 方法:

// execute the query
EKCalendarItem[] events = App.Current.EventStore.EventsMatching ( query );

請注意,查詢是同步的(封鎖),而且可能需要很長的時間,視查詢而定,因此您可能想要啟動新的線程或工作來執行它。

搜尋提醒

搜尋提醒類似於事件;它需要述詞,但呼叫已經是異步的,所以您不必擔心封鎖線程:

// create our NSPredicate which we'll use for the query
NSPredicate query = App.Current.EventStore.PredicateForReminders ( null );

// execute the query
App.Current.EventStore.FetchReminders (
        query, ( EKReminder[] items ) => {
                // do someting with the items
        } );

摘要

本檔概述 EventKit 架構的重要部分,以及一些最常見的工作。 不過,EventKit 架構非常龐大且強大,且包含此處尚未引進的功能,例如:批次更新、設定警示、設定事件週期、註冊及接聽行事曆資料庫變更、設定 GeoFences 等。 如需詳細資訊,請參閱Apple的 行事曆和提醒程序設計指南