EventKit в Xamarin.iOS

iOS имеет два встроенных приложения, связанных с календарем: приложение календаря и приложение напоминаний. Достаточно просто понять, как приложение календаря управляет данными календаря, но приложение напоминаний становится менее очевидным. Напоминания могут на самом деле иметь даты, связанные с ними с точки зрения того, когда они должны быть выполнены, когда они завершены, и т. д. Таким образом, iOS сохраняет все данные календаря, будь то события календаря или напоминания, в одном расположении называется базой данных календаря.

Платформа EventKit предоставляет способ доступа к данным календарей, событий календаря и напоминаний , которые хранятся в базе данных календаря. Доступ к календарям и событиям календаря доступен с iOS 4, но доступ к напоминаниям является новым в iOS 6.

В этом руководстве мы рассмотрим следующее:

  • Основные сведения о EventKit. Это позволит ознакомиться с основными классами EventKit и понять их использование. Этот раздел требуется прочитать перед решением следующей части документа.
  • Общие задачи. Общие задачи предназначены для быстрого получения сведений о том, как выполнять общие действия , такие как перечисление календарей, создание, сохранение и получение событий календаря и напоминаний, а также использование встроенных контроллеров для создания и изменения событий календаря. Этот раздел не должен быть считываемым интерфейсом на спину, так как он должен быть ссылкой для конкретных задач.

Все задачи в этом руководстве доступны в примере приложения-компаньона:

The companion sample application screens

Требования

EventKit появился в iOS 4.0, но доступ к данным напоминаний появился в iOS 6.0. Таким образом, чтобы сделать общую разработку EventKit, необходимо настроить по крайней мере версию 4.0 и 6.0 для напоминаний.

Кроме того, приложение "Напоминания" недоступно на симуляторе, что означает, что данные напоминаний также не будут доступны, если вы не добавите их в первую очередь. Кроме того, запросы на доступ отображаются только пользователю на фактическом устройстве. Таким образом, разработка EventKit лучше всего тестируется на устройстве.

Основные сведения о наборе событий

При работе с EventKit важно понимать общие классы и их использование. Все эти классы можно найти в EventKit и EventKitUI (для 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;

Обратите внимание, что все примеры, приведенные здесь, используют этот шаблон, поэтому они ссылаются на EventStore этот App.Current.EventStoreшаблон.

Запрос доступа к данным календаря и напоминания

Прежде чем разрешить доступ к любым данным через EventStore, приложение должно сначала запросить доступ к данным событий календаря или напоминаниям, в зависимости от того, какой из них требуется. Чтобы упростить эту задачу, предоставляет EventStore метод, который RequestAccess ( при вызове) будет отображать представление оповещений пользователю, сообщающее им, что приложение запрашивает доступ к данным календаря или данным напоминания, в зависимости от того, что EKEntityType передается в него. Так как он вызывает представление оповещений, вызов является асинхронным, и вызовет обработчик завершения, переданный в качестве NSAction (или лямбда)), который получит два параметра; логическое значение, указывающее, предоставлен ли доступ, и 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 ();
            } );

После предоставления запроса оно будет запоминаться до тех пор, пока приложение установлено на устройстве и не появится оповещение пользователю. Однако доступ предоставляется только типу ресурса, событиям календаря или напоминаниям, предоставленным. Если приложению требуется доступ к обоим приложениям, он должен запросить оба.

Так как разрешение запоминается, это относительно дешево, чтобы каждый раз сделать запрос, поэтому рекомендуется всегда запрашивать доступ перед выполнением операции.

Кроме того, так как обработчик завершения вызывается в отдельном потоке (не пользовательского интерфейса), все обновления пользовательского интерфейса в обработчике завершения должны вызываться с помощью InvokeOnMainThread, в противном случае исключение будет создано, а если не поймано, приложение завершится сбоем.

EKEntityType

EKEntityType — это перечисление, описывающее тип EventKit элемента или данных. Он имеет два значения: Event и Напоминание. Он используется в нескольких методах, включая EventStore.RequestAccess указание EventKit типа данных для получения доступа к или извлечению.

EKCalendar

EKCalendar представляет календарь, содержащий группу событий календаря. Календари можно хранить в разных местах, таких как локально, в iCloud, в расположении поставщика сторонних поставщиков, таких как Exchange Server или Google и т. д. Много раз EKCalendar используется для того, чтобы узнать EventKit , где искать события или где их сохранить.

EKEventEditController

EKEventEditController можно найти в EventKitUI пространстве имен и является встроенным контроллером, который можно использовать для редактирования или создания событий календаря. Как и встроенные контроллеры камеры, EKEventEditController выполняет тяжелый подъем для вас при отображении пользовательского интерфейса и обработке сохранения.

EKEvent

EKEvent представляет событие календаря. Оба EKEvent и EKReminder наследуется от EKCalendarItem и имеют такие поля, как Title, Notesи т. д.

EKReminder

EKReminder представляет элемент напоминания.

EKSpan

EKSpan — это перечисление, описывающее диапазон событий при изменении событий, которые могут рекурсировать, и имеет два значения: ThisEvent и FutureEvents. ThisEvent означает, что любые изменения будут происходить только к конкретному событию в серии, на которое ссылается ссылка, в то время как FutureEvents это событие и все будущие повторения.

Задачи

Для простоты использования EventKit было разбито на распространенные задачи, описанные в следующих разделах.

Перечисление календарей

Чтобы перечислить календари, настроенные пользователем на устройстве, вызовите GetCalendarsEventStore и передайте тип календарей (напоминания или события), которые вы хотите получить:

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

Добавление или изменение события с помощью встроенного контроллера

EKEventEditViewController делает много тяжелого подъема, если вы хотите создать или изменить событие с тем же пользовательским интерфейсом, который представлен пользователю при использовании приложения календаря:

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

Чтобы использовать его, необходимо объявить его как переменную уровня класса, чтобы она не собирала мусор, если она объявлена в методе:

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

Если вы хотите предварительно заполнить пользовательский интерфейс, обязательно задайте свойство 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 методе для изменения события и повторного изменения или выполнения других действий, если оно отменено, 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;
        }
}

Создание события программным способом

Чтобы создать событие в коде, используйте метод фабрики FromStore в EKEvent классе и задайте для него все данные:

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;

Чтобы сохранить событие, вызовите метод SaveEvent в :EventStore

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;

Чтобы сохранить, вызовите метод SaveReminder в :EventStore

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

Получение события по идентификатору

Чтобы получить событие по идентификатору, используйте метод EventFromIdentifier в нем EventStore и передайте его, EventIdentifier который был извлечен из события:

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

Для событий существует два других свойства идентификатора, но EventIdentifier это единственный, который работает для этого.

Получение напоминания по идентификатору

Чтобы получить напоминание, используйте метод GetCalendarItem в нем EventStore и передайте его в CalendarItemIdentifier:

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

Так как GetCalendarItem возвращается объект EKCalendarItem, он должен быть приведение EKReminder , если необходимо получить доступ к данным напоминания или использовать экземпляр как более поздний EKReminder .

Не используйте GetCalendarItem для событий календаря, как и во время написания, он не работает.

Удаление события

Чтобы удалить событие календаря, вызовите RemoveEvent для события EventStore и передайте ссылку на событие и соответствующееEKSpan:

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

Однако обратите внимание, что после удаления события ссылка на событие будет nullуказана.

Удаление напоминания

Чтобы удалить напоминание, вызовите RemoveReminder и EventStore передайте ссылку на напоминание:

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

Обратите внимание, что в приведенном выше коде имеется приведение EKReminder, так как GetCalendarItem он использовался для извлечения.

Поиск событий

Чтобы выполнить поиск событий календаря, необходимо создать объект NSPredicate с помощью метода PredicateForEvents в объекте EventStore. Объект 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используйте метод EventsMatching в следующих EventStoreпараметрах:

// 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.