EventKit w środowisku Xamarin.iOS

System iOS ma wbudowane dwie aplikacje związane z kalendarzem: aplikację kalendarza i aplikację przypomnień. Wystarczy łatwo zrozumieć, jak aplikacja kalendarza zarządza danymi kalendarza, ale aplikacja przypomnień jest mniej oczywista. Przypomnienia mogą mieć skojarzone z nimi daty w zakresie czasu ich ukończenia, czasu ich ukończenia itp. W związku z tym system iOS przechowuje wszystkie dane kalendarza, niezależnie od tego, czy są to zdarzenia kalendarza, czy przypomnienia, w jednej lokalizacji nazywanej bazą danych kalendarza.

Struktura EventKit umożliwia uzyskiwanie dostępu do danych kalendarzy, zdarzeń kalendarza i przypomnień przechowywanych przez bazę danych kalendarza. Dostęp do kalendarzy i wydarzeń kalendarza jest dostępny od systemu iOS 4, ale dostęp do przypomnień jest nowy w systemie iOS 6.

W tym przewodniku omówimy następujące zagadnienia:

  • EventKit Basics — spowoduje to wprowadzenie podstawowych elementów zestawu EventKit za pośrednictwem głównych klas i zrozumienie ich użycia. Ta sekcja jest wymagana przed zajęciem się następną częścią dokumentu.
  • Typowe zadania — sekcja typowych zadań jest przeznaczona do szybkiego dokumentacji dotyczącej wykonywania typowych czynności, takich jak: wyliczanie kalendarzy, tworzenie, zapisywanie i pobieranie zdarzeń kalendarza oraz przypomnień, a także używanie wbudowanych kontrolerów do tworzenia i modyfikowania zdarzeń kalendarza. Ta sekcja nie musi być odczytywana z przodu do tyłu, ponieważ ma być odwołaniem do określonych zadań.

Wszystkie zadania w tym przewodniku są dostępne w przykładowej aplikacji towarzyszącej:

The companion sample application screens

Wymagania

Zestaw EventKit został wprowadzony w systemie iOS 4.0, ale dostęp do danych Przypomnienia został wprowadzony w systemie iOS 6.0. W związku z tym w celu ogólnego opracowywania zestawu EventKit należy kierować do przypomnień co najmniej wersję 4.0 i 6.0.

Ponadto aplikacja Przypomnienia nie jest dostępna w symulatorze, co oznacza, że dane przypomnienia również nie będą dostępne, chyba że zostaną one dodane jako pierwsze. Ponadto żądania dostępu są wyświetlane tylko użytkownikowi na rzeczywistym urządzeniu. W związku z tym programowanie zestawu EventKit najlepiej sprawdza się na urządzeniu.

Event Kit Basics

Podczas pracy z zestawem EventKit ważne jest zrozumienie typowych klas i ich użycia. Wszystkie te klasy można znaleźć w polach EventKit i EventKitUI (dla elementu EKEventEditController).

Magazyn zdarzeń

Klasa EventStore jest najważniejszą klasą w zestawie EventKit, ponieważ jest wymagana do wykonywania jakichkolwiek operacji w zestawie EventKit. Można go traktować jako magazyn trwały lub aparat bazy danych dla wszystkich danych zestawu EventKit. Z EventStore poziomu dostępu do kalendarzy i zdarzeń kalendarza w aplikacji kalendarza oraz przypomnień w aplikacji Przypomnienia.

Ze względu EventStore na to, że aparat bazy danych powinien być długotrwały, co oznacza, że należy go utworzyć i zniszczyć tak mało, jak to możliwe w okresie istnienia wystąpienia aplikacji. W rzeczywistości zaleca się, aby po utworzeniu jednego wystąpienia EventStore w aplikacji zachować to odwołanie przez cały okres istnienia aplikacji, chyba że nie będziesz tego potrzebować ponownie. ponadto wszystkie wywołania powinny przejść do pojedynczego EventStore wystąpienia. Z tego powodu zaleca się zachowanie dostępnego pojedynczego wystąpienia.

Tworzenie magazynu zdarzeń

Poniższy kod ilustruje wydajny sposób tworzenia pojedynczego EventStore wystąpienia klasy i udostępniania go statycznie z poziomu aplikacji:

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 ( );
    }
}

Powyższy kod używa wzorca Singleton do utworzenia wystąpienia podczas EventStore ładowania aplikacji. Dostęp EventStore do obiektu można uzyskać globalnie z poziomu aplikacji w następujący sposób:

App.Current.EventStore;

Zwróć uwagę, że wszystkie przykłady w tym miejscu używają tego wzorca, więc odwołują się do EventStore elementu za pomocą metody App.Current.EventStore.

Żądanie dostępu do danych kalendarza i przypomnienia

Przed zezwoleniem na dostęp do jakichkolwiek danych za pośrednictwem magazynu zdarzeń aplikacja musi najpierw zażądać dostępu do danych zdarzeń kalendarza lub danych przypomnień, w zależności od potrzeb. Aby to ułatwić, EventStore uwidacznia metodę o nazwie RequestAccess , która — po wywołaniu — wyświetli użytkownikowi widok alertu informujący o tym, że aplikacja żąda dostępu do danych kalendarza lub danych przypomnienia, w zależności od tego, które EKEntityType dane są przekazywane do niego. Ponieważ zgłasza widok alertu, wywołanie jest asynchroniczne i wywoła procedurę obsługi uzupełniania przekazaną NSAction jako element (lub lambda), który otrzyma dwa parametry; wartość logiczną określającą, czy udzielono dostępu, oraz NSErrorparametr , który, jeśli nie ma wartości null, będzie zawierać jakiekolwiek informacje o błędzie w żądaniu. Na przykład następujące kodowane dane będą żądać dostępu do danych zdarzeń kalendarza i wyświetlać widok alertu, jeśli żądanie nie zostało przyznane.

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 ();
            } );

Po udzieleniu żądania zostanie zapamiętany tak długo, jak aplikacja jest zainstalowana na urządzeniu i nie wyświetli alertu dla użytkownika. Jednak dostęp jest udzielany tylko dla typu zasobu, wydarzeń kalendarza lub przypomnień udzielonych. Jeśli aplikacja potrzebuje dostępu do obu tych elementów, powinna zażądać obu tych elementów.

Ponieważ uprawnienia są zapamiętywane, stosunkowo tanie jest wykonywanie żądania za każdym razem, dlatego warto zawsze żądać dostępu przed wykonaniem operacji.

Ponadto, ponieważ procedura obsługi uzupełniania jest wywoływana w osobnym wątku (innym niż interfejs użytkownika), wszystkie aktualizacje interfejsu użytkownika w procedurze obsługi uzupełniania powinny być wywoływane za pośrednictwem metody InvokeOnMainThread, w przeciwnym razie zostanie zgłoszony wyjątek, a jeśli nie zostanie przechwycony, aplikacja ulegnie awarii.

Typ EKEntityType

EKEntityType to wyliczenie opisujące typ EventKit elementu lub danych. Ma dwie wartości: Event i Reminder. Jest on używany w wielu metodach, w tym EventStore.RequestAccess do określenia EventKit , jakiego rodzaju dane mają uzyskać dostęp do lub pobrać.

EKCalendar

EKCalendar reprezentuje kalendarz, który zawiera grupę zdarzeń kalendarza. Kalendarze mogą być przechowywane w wielu różnych miejscach, takich jak lokalnie, w usłudze iCloud, w lokalizacji dostawcy innej firmy, takiej jak Exchange Server lub Google itp. Wiele razy EKCalendar służy do określania EventKit miejsca wyszukiwania zdarzeń lub miejsca ich zapisywania.

EKEventEditController

EKEventEditController można znaleźć w EventKitUI przestrzeni nazw i jest wbudowanym kontrolerem, który może służyć do edytowania lub tworzenia zdarzeń kalendarza. Podobnie jak w przypadku wbudowanych kontrolerów kamer, EKEventEditController bardzo duże obciążenie podczas wyświetlania interfejsu użytkownika i obsługi zapisywania.

EKEvent

EKEvent reprezentuje zdarzenie kalendarza. Zarówno EKEvent , jak i EKReminder dziedziczą z EKCalendarItem pól, takich jak Title, Notesi tak dalej.

EKReminder

EKReminder reprezentuje element przypomnienia.

EKSpan

EKSpan to wyliczenie opisujące zakres zdarzeń podczas modyfikowania zdarzeń, które mogą się powtarzać, i ma dwie wartości: ThisEvent i FutureEvents. ThisEvent oznacza, że wszelkie zmiany zostaną wprowadzone tylko do określonego zdarzenia w serii, do którego się odwołujesz, natomiast FutureEvents wpłynie to na to zdarzenie i wszystkie przyszłe cykle.

Zadania

W celu ułatwienia użycia użycie zestawu EventKit zostało podzielone na typowe zadania opisane w poniższych sekcjach.

Wyliczanie kalendarzy

Aby wyliczyć kalendarze skonfigurowane przez użytkownika na urządzeniu, wywołaj metodę GetCalendarsEventStore i przekaż typ kalendarzy (przypomnienia lub zdarzenia), które chcesz otrzymać:

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

Dodawanie lub modyfikowanie zdarzenia przy użyciu wbudowanego kontrolera

Kontroler EKEventEditViewController wykonuje wiele operacji podnoszenia, jeśli chcesz utworzyć lub edytować zdarzenie za pomocą tego samego interfejsu użytkownika, który jest prezentowany użytkownikowi podczas korzystania z aplikacji kalendarza:

The UI that is presented to the user when using the Calendar Application

Aby go użyć, należy zadeklarować ją jako zmienną na poziomie klasy, aby nie pobierała pamięci, jeśli jest zadeklarowana w metodzie:

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

Następnie, aby go uruchomić: utwórz wystąpienie, nadaj mu odwołanie do EventStore, podłącz do niego delegata EKEventEditViewDelegate , a następnie wyświetl go przy użyciu polecenia 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 );

Opcjonalnie, jeśli chcesz wstępnie wypełnić zdarzenie, możesz utworzyć nowe zdarzenie (jak pokazano poniżej) lub pobrać zapisane zdarzenie:

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.”;

Jeśli chcesz wstępnie wypełnić interfejs użytkownika, upewnij się, że właściwość Event na kontrolerze:

eventController.Event = newEvent;

Aby użyć istniejącego zdarzenia, zobacz sekcję Pobieranie zdarzenia według identyfikatora w dalszej części.

Delegat powinien zastąpić metodę Completed , która jest wywoływana przez kontroler po zakończeniu pracy użytkownika z okna dialogowego:

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);
                }
        }
}

Opcjonalnie w delegu możesz sprawdzić akcję w metodzie Completed , aby zmodyfikować zdarzenie i ponownie zapisać lub wykonać inne czynności, jeśli została anulowana, itpetera:

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;
        }
}

Programowe tworzenie zdarzenia

Aby utworzyć zdarzenie w kodzie, użyj metody Fabryki FromStore w EKEvent klasie i ustaw na nim dowolne dane:

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.";

Musisz ustawić kalendarz, w którym ma być zapisane zdarzenie, ale jeśli nie masz preferencji, możesz użyć wartości domyślnej:

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

Aby zapisać zdarzenie, wywołaj metodę SaveEvent w pliku EventStore:

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

Po zapisaniu właściwość EventIdentifier zostanie zaktualizowana o unikatowy identyfikator, którego można użyć później do pobrania zdarzenia:

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

EventIdentifier jest ciągiem sformatowanym identyfikatorem GUID.

Programowe tworzenie przypomnienia

Tworzenie przypomnienia w kodzie jest takie samo jak tworzenie zdarzenia kalendarza:

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

Aby zapisać, wywołaj metodę SaveReminder w pliku EventStore:

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

Pobieranie zdarzenia według identyfikatora

Aby pobrać zdarzenie według jego identyfikatora, użyj metody EventFromIdentifier w obiekcie EventStore i przekaż zdarzenie EventIdentifier , które zostało pobrane ze zdarzenia:

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

W przypadku zdarzeń istnieją dwie inne właściwości identyfikatora, ale EventIdentifier jest jedyną, która działa w tym celu.

Pobieranie przypomnienia według identyfikatora

Aby pobrać przypomnienie, użyj metody GetCalendarItem w obiekcie EventStore i przekaż ją do elementu CalendarItemIdentifier:

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

Ponieważ GetCalendarItem zwraca element EKCalendarItem, należy go rzutować, aby EKReminder uzyskać dostęp do danych przypomnienia lub użyć wystąpienia jako późniejszego EKReminder .

Nie używaj GetCalendarItem w przypadku zdarzeń kalendarza, jak w momencie pisania, nie działa.

Usuwanie zdarzenia

Aby usunąć zdarzenie kalendarza, wywołaj metodę RemoveEvent na urządzeniu EventStore i przekaż odwołanie do zdarzenia oraz odpowiednie polecenie EKSpan:

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

Pamiętaj jednak, że po usunięciu zdarzenia odwołanie do zdarzenia będzie miało wartość null.

Usuwanie przypomnienia

Aby usunąć przypomnienie, wywołaj metodę RemoveReminder na obiekcie EventStore i przekaż odwołanie do przypomnienia:

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

Zwróć uwagę, że w powyższym kodzie istnieje rzutowanie do EKReminderelementu , ponieważ GetCalendarItem użyto go do pobrania

Wyszukiwanie zdarzeń

Aby wyszukać zdarzenia kalendarza, należy utworzyć obiekt NSPredicate za pomocą metody PredicateForEvents w pliku EventStore. Obiekt NSPredicate jest obiektem danych zapytania używanym przez system iOS do lokalizowania dopasowań:

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 );

Po utworzeniu NSPredicatemetody użyj metody EventsMatching w pliku EventStore:

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

Należy pamiętać, że zapytania są synchroniczne (blokowanie) i mogą zająć dużo czasu, w zależności od zapytania, więc możesz chcieć uruchomić nowy wątek lub zadanie, aby to zrobić.

Wyszukiwanie przypomnień

Wyszukiwanie przypomnień jest podobne do zdarzeń; wymaga predykatu, ale wywołanie jest już asynchroniczne, więc nie musisz martwić się o blokowanie wątku:

// 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
        } );

Podsumowanie

W tym dokumencie omówiono zarówno ważne elementy struktury EventKit, jak i szereg najbardziej typowych zadań. Jednak struktura EventKit jest bardzo duża i zaawansowana i zawiera funkcje, które nie zostały tu wprowadzone, takie jak: aktualizacje wsadowe, konfigurowanie alarmów, konfigurowanie cyklu zdarzeń, rejestrowanie i nasłuchiwanie zmian w bazie danych kalendarza, ustawianie geofencingów i nie tylko. Aby uzyskać więcej informacji, zobacz Kalendarz i przypomnienia firmy Apple — przewodnik programowania.