Контакты и контакты в Xamarin.iOS

В этой статье рассматривается работа с новыми платформами пользовательского интерфейса контактов и контактов в приложении Xamarin.iOS. Эти платформы заменяют существующий пользовательский интерфейс адресной книги и адресной книги, используемый в предыдущих версиях iOS.

С введением iOS 9 Apple выпустила две новые платформы, Contacts и ContactsUI, заменив существующие платформы адресной книги и пользовательского интерфейса адресной книги, используемые iOS 8 и более ранними версиями.

Две новые платформы содержат следующие функции:

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

  • ContactsUI — предоставляет элементы пользовательского интерфейса Xamarin.iOS для отображения, изменения, выбора и создания контактов на устройствах iOS.

An example Contact Sheet on an iOS device

Внимание

Существующие AddressBook и AddressBookUI платформы, используемые iOS 8 (и более поздней версии), устарели в iOS 9 и должны быть заменены новыми Contacts и ContactsUI платформами как можно скорее для любого существующего приложения Xamarin.iOS. Новые приложения должны быть написаны на новых платформах.

В следующих разделах мы рассмотрим эти новые платформы и как реализовать их в приложении Xamarin.iOS.

Платформа контактов

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

Объекты контакта

Класс CNContact предоставляет потокобезопасный доступ только для чтения к свойствам контакта, таким как имя, адрес или номера Телефон. CNContact функции, такие как и NSDictionary содержат несколько, доступных только для чтения коллекций свойств (например, адресов или номеров телефонов):

Contact Object overview

Для любого свойства, которое может иметь несколько значений (например, адрес электронной почты или номера телефона), они будут представлены в виде массива NSLabeledValue объектов. NSLabeledValue — это потокобезопасный кортеж, состоящий из набора меток только для чтения и значений, где метка определяет значение для пользователя (например, домашняя или рабочая почта). Платформа контактов предоставляет выбор предопределенных меток (с помощью CNLabelKey и CNLabelPhoneNumberKey статических классов), которые можно использовать в приложении или вы можете определить пользовательские метки для ваших потребностей.

Для любого приложения Xamarin.iOS, необходимого для настройки значений существующего контакта (или создания новых), используйте NSMutableContact версию класса и его подклассы (например CNMutablePostalAddress).

Например, следующий код создаст новый контакт и добавит его в коллекцию контактов пользователя:

// Create a new Mutable Contact (read/write)
var contact = new CNMutableContact();

// Set standard properties
contact.GivenName = "John";
contact.FamilyName = "Appleseed";

// Add email addresses
var homeEmail = new CNLabeledValue<NSString>(CNLabelKey.Home, new NSString("john.appleseed@mac.com"));
var workEmail = new CNLabeledValue<NSString>(CNLabelKey.Work, new NSString("john.appleseed@apple.com"));
contact.EmailAddresses = new CNLabeledValue<NSString>[] { homeEmail, workEmail };

// Add phone numbers
var cellPhone = new CNLabeledValue<CNPhoneNumber>(CNLabelPhoneNumberKey.iPhone, new CNPhoneNumber("713-555-1212"));
var workPhone = new CNLabeledValue<CNPhoneNumber>("Work", new CNPhoneNumber("408-555-1212"));
contact.PhoneNumbers = new CNLabeledValue<CNPhoneNumber>[] { cellPhone, workPhone };

// Add work address
var workAddress = new CNMutablePostalAddress()
{
    Street = "1 Infinite Loop",
    City = "Cupertino",
    State = "CA",
    PostalCode = "95014"
};
contact.PostalAddresses = new CNLabeledValue<CNPostalAddress>[] { new CNLabeledValue<CNPostalAddress>(CNLabelKey.Work, workAddress) };

// Add birthday
var birthday = new NSDateComponents()
{
    Day = 1,
    Month = 4,
    Year = 1984
};
contact.Birthday = birthday;

// Save new contact
var store = new CNContactStore();
var saveRequest = new CNSaveRequest();
saveRequest.AddContact(contact, store.DefaultContainerIdentifier);

// Attempt to save changes
NSError error;
if (store.ExecuteSaveRequest(saveRequest, out error))
{
    Console.WriteLine("New contact saved");
}
else
{
    Console.WriteLine("Save error: {0}", error);
}

Если этот код выполняется на устройстве iOS 9, новый контакт будет добавлен в коллекцию пользователя. Например:

A new contact added to the user's collection

Форматирование контактов и локализация

Платформа "Контакты" содержит несколько объектов и методов, которые помогают форматировать и локализовать содержимое для отображения пользователю. Например, следующий код будет правильно форматировать имя контактов и адрес рассылки для отображения:

Console.WriteLine(CNContactFormatter.GetStringFrom(contact, CNContactFormatterStyle.FullName));
Console.WriteLine(CNPostalAddressFormatter.GetStringFrom(workAddress, CNPostalAddressFormatterStyle.MailingAddress));

Для меток свойств, отображаемых в пользовательском интерфейсе приложения, платформа Contact имеет методы локализации этих строк. Опять же, это основано на текущем языковом стандарте устройства iOS, на который выполняется приложение. Например:

// Localized properties
Console.WriteLine(CNContact.LocalizeProperty(CNContactOptions.Nickname));
Console.WriteLine(CNLabeledValue<NSString>.LocalizeLabel(CNLabelKey.Home));

Извлечение существующих контактов

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

Используя предикаты (созданные из CNContact класса), можно фильтровать результаты, возвращаемые при получении контактов из базы данных. Чтобы получить только контакты, содержащие строку Appleseed, используйте следующий код:

// Create predicate to locate requested contact
var predicate = CNContact.GetPredicateForContacts("Appleseed");

Внимание

Универсальные и составные предикаты не поддерживаются платформой контактов.

Например, чтобы ограничить получение только свойствами GivenName и FamilyName контакта, используйте следующий код:

// Define fields to be searched
var fetchKeys = new NSString[] {CNContactKey.GivenName, CNContactKey.FamilyName};

Наконец, чтобы искать базу данных и возвращать результаты, используйте следующий код:

// Grab matching contacts
var store = new CNContactStore();
NSError error;
var contacts = store.GetUnifiedContacts(predicate, fetchKeys, out error);

Если этот код был запущен после примера, созданного в приведенном выше разделе "Объекты контактов", он вернет только что созданный контакт John Appleseed.

Конфиденциальность контактов

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

Запрос на разрешение будет представлен только один раз, при первом запуске приложения и последующих запусках или вызовах для CNContactStore использования разрешения, выбранного пользователем в то время.

Вы должны разработать приложение таким образом, чтобы он корректно обрабатывал пользователя, отказывающего в доступе к базе данных контактов.

Получение частичных контактов

Частичный контакт — это контакт , для которых извлекается только часть доступных свойств из хранилища контактов. Если вы пытаетесь получить доступ к свойству, которое ранее не было возвращено, это приведет к исключению.

Вы можете легко проверка, чтобы узнать, имеет ли данный контакт требуемое свойство с помощью либо IsKeyAvailableAreKeysAvailable методов экземпляраCNContact. Например:

// Does the contact contain the requested key?
if (!contact.IsKeyAvailable(CNContactOption.PostalAddresses)) {
    // No, re-request to pull required info
    var fetchKeys = new NSString[] {CNContactKey.GivenName, CNContactKey.FamilyName, CNContactKey.PostalAddresses};
    var store = new CNContactStore();
    NSError error;
    contact = store.GetUnifiedContact(contact.Identifier, fetchKeys, out error);
}

Внимание

GetUnifiedContacts Методы CNContactStoreGetUnifiedContact класса возвращают только частичный контакт, ограниченный свойствами, запрашиваемыми из предоставленных ключей получения.

Объединенные контакты

У пользователя могут быть разные источники контактных данных для одного человека в базе данных контактов (например, iCloud, Facebook или Google Mail). В приложениях iOS и OS X эти контактные данные автоматически будут связаны и отображаются пользователю в виде единого единого контакта:

Unified Contacts overview

Этот единый контакт — это временное представление контактных данных ссылки в памяти, которое будет присвоено собственному уникальному идентификатору (которое должно использоваться для ссылки при необходимости). По умолчанию платформа "Контакты" возвращает единый контакт по возможности.

Создание и обновление контактов

Как мы видели в приведенном выше разделе "Объекты контактов", вы используете CNContactStore и экземпляр CNMutableContact объекта для создания новых контактов, которые затем записываются в базу данных контактов пользователя с помощью CNSaveRequest:

// Create a new Mutable Contact (read/write)
var contact = new CNMutableContact();

// Set standard properties
contact.GivenName = "John";
contact.FamilyName = "Appleseed";

// Save new contact
var store = new CNContactStore();
var saveRequest = new CNSaveRequest();
saveRequest.AddContact(contact, store.DefaultContainerIdentifier);

NSError error;
if (store.ExecuteSaveRequest(saveRequest, out error)) {
    Console.WriteLine("New contact saved");
} else {
    Console.WriteLine("Save error: {0}", error);
}

Можно CNSaveRequest также использовать для кэширования нескольких изменений контактов и групп в одну операцию и пакетировать эти изменения CNContactStore.

Чтобы обновить неизменяемый контакт, полученный из операции получения, необходимо сначала запросить изменяемую копию, которую затем изменить и сохранить обратно в хранилище контактов. Например:

// Get mutable copy of contact
var mutable = contact.MutableCopy() as CNMutableContact;
var newEmail = new CNLabeledValue<NSString>(CNLabelKey.Home, new NSString("john.appleseed@xamarin.com"));

// Append new email
var emails = new NSObject[mutable.EmailAddresses.Length+1];
mutable.EmailAddresses.CopyTo(emails,0);
emails[mutable.EmailAddresses.Length+1] = newEmail;
mutable.EmailAddresses = emails;

// Update contact
var store = new CNContactStore();
var saveRequest = new CNSaveRequest();
saveRequest.UpdateContact(mutable);

NSError error;
if (store.ExecuteSaveRequest(saveRequest, out error)) {
    Console.WriteLine("Contact updated.");
} else {
    Console.WriteLine("Update error: {0}", error);
}

Уведомления об изменениях контактов

Всякий раз, когда контакт изменяется, хранилище контактов публикует в CNContactStoreDidChangeNotification Центре уведомлений по умолчанию. Если вы кэшировали или отображают какие-либо контакты, необходимо обновить эти объекты из хранилища контактов (CNContactStore).

Контейнеры и группы

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

Containers and Groups overview

Некоторые контейнеры позволяют упорядочивать контакты в одну или несколько групп или подгрупп. Это поведение зависит от резервного хранилища для данного контейнера. Например, iCloud имеет только один контейнер, но может иметь множество групп (но не подгрупп). С другой стороны, Microsoft Exchange не поддерживает группы, но может иметь несколько контейнеров (по одному для каждой папки Exchange).

Overlap within Containers and Groups

Платформа ContactsUI

В ситуациях, когда приложению не требуется представить пользовательский интерфейс, можно использовать платформу ContactsUI для отображения, редактирования, выбора и создания контактов в приложении Xamarin.iOS.

Используя встроенные элементы управления Apple, вы не только сокращаете объем кода, который необходимо создать для поддержки контактов в приложении Xamarin.iOS, но и представляет согласованный интерфейс для пользователей приложения.

Контроллер представления средства выбора контактов

Контроллер представления выбора контактов (CNContactPickerViewController) управляет стандартным представлением выбора контактов, которое позволяет пользователю выбирать контакт или свойство Contact из базы данных контактов пользователя. Пользователь может выбрать один или несколько контактов (на основе его использования), а контроллер представления выбора контактов не запрашивает разрешение перед отображением средства выбора.

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

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

using System;
using System.Linq;
using UIKit;
using Foundation;
using Contacts;
using ContactsUI;

namespace iOS9Contacts
{
    public class ContactPickerDelegate: CNContactPickerDelegate
    {
        #region Constructors
        public ContactPickerDelegate ()
        {
        }

        public ContactPickerDelegate (IntPtr handle) : base (handle)
        {
        }
        #endregion

        #region Override Methods
        public override void ContactPickerDidCancel (CNContactPickerViewController picker)
        {
            Console.WriteLine ("User canceled picker");

        }

        public override void DidSelectContact (CNContactPickerViewController picker, CNContact contact)
        {
            Console.WriteLine ("Selected: {0}", contact);
        }

        public override void DidSelectContactProperty (CNContactPickerViewController picker, CNContactProperty contactProperty)
        {
            Console.WriteLine ("Selected Property: {0}", contactProperty);
        }
        #endregion
    }
}

Чтобы разрешить пользователю выбрать адрес электронной почты из контактов в базе данных, можно использовать следующий код:

// Create a new picker
var picker = new CNContactPickerViewController();

// Select property to pick
picker.DisplayedPropertyKeys = new NSString[] {CNContactKey.EmailAddresses};
picker.PredicateForEnablingContact = NSPredicate.FromFormat("emailAddresses.@count > 0");
picker.PredicateForSelectionOfContact = NSPredicate.FromFormat("emailAddresses.@count == 1");

// Respond to selection
picker.Delegate = new ContactPickerDelegate();

// Display picker
PresentViewController(picker,true,null);

Контроллер представления контактов

Класс "КонтроллерCNContactViewController представления контактов" предоставляет контроллеру для представления стандартного представления контактов конечному пользователю. Представление контакта может отображать новые, неизвестные или существующие контакты, а тип должен быть указан перед отображением представления путем вызова правильного статического конструктора (FromNewContact, FromUnknownContact, ). FromContact Пример:

// Create a new contact view
var view = CNContactViewController.FromContact(contact);

// Display the view
PresentViewController(view, true, null);

Итоги

В этой статье подробно рассматривается работа с платформами пользовательского интерфейса контактов и контактов в приложении Xamarin.iOS. Во-первых, он охватывал различные типы объектов, которые предоставляет платформа Contact и как они используются для создания новых или доступа к существующим контактам. Она также изучила платформу пользовательского интерфейса контактов, чтобы выбрать существующие контакты и отобразить контактные данные.