Partilhar via


Contatos e ContatosUI no Xamarin.iOS

Este artigo aborda o trabalho com as novas estruturas de interface do usuário Contatos e Contatos em um aplicativo Xamarin.iOS. Essas estruturas substituem a interface do usuário existente do Catálogo de Endereços e do Catálogo de Endereços usada em versões anteriores do iOS.

Com a introdução do iOS 9, a Apple lançou duas novas estruturas, Contacts e ContactsUI, que substituem as estruturas existentes de Catálogo de Endereços e Catálogo de Endereços usadas pelo iOS 8 e anteriores.

As duas novas estruturas contêm a seguinte funcionalidade:

  • Contatos - Fornece acesso aos dados da lista de contatos do usuário. Como a maioria dos aplicativos requer apenas acesso somente leitura, essa estrutura foi otimizada para acesso seguro de thread e somente leitura.

  • ContactsUI - Fornece elementos da interface do usuário do Xamarin.iOS para exibir, editar, selecionar e criar contatos em dispositivos iOS.

Um exemplo de Folha de Contatos em um dispositivo iOS

Importante

Os modelos existentes AddressBook e AddressBookUI de uso do iOS 8 (e anteriores) foram preteridos no iOS 9 e devem ser substituídos pelos novos Contacts e ContactsUI frameworks o mais rápido possível para qualquer aplicativo Xamarin.iOS existente. Novos aplicativos devem ser escritos em relação às novas estruturas.

Nas seções a seguir, veremos essas novas estruturas e como implementá-las em um aplicativo Xamarin.iOS.

A estrutura de contatos

O Contacts Framework fornece ao Xamarin.iOS acesso às informações de contato do usuário. Como a maioria dos aplicativos requer apenas acesso somente leitura, essa estrutura foi otimizada para acesso seguro de thread e somente leitura.

Objetos de contato

A CNContact classe fornece acesso seguro de thread, somente leitura às propriedades de um contato, como Nome, Endereço ou Números de Telefone. CNContact funções como a NSDictionary e contém várias coleções somente leitura de propriedades (como endereços ou números de telefone):

Visão geral do objeto de contato

Para qualquer propriedade que possa ter vários valores (como endereço de email ou números de telefone), eles serão representados como uma matriz de NSLabeledValue objetos. NSLabeledValue é uma tupla thread safe que consiste em um conjunto somente leitura de rótulos e valores onde o rótulo define o valor para o usuário (por exemplo, email doméstico ou de trabalho). A estrutura Contatos fornece uma seleção de rótulos predefinidos (por meio CNLabelKey das classes estáticas e CNLabelPhoneNumberKey outras) que você pode usar em seu aplicativo ou tem a opção de definir rótulos personalizados para suas necessidades.

Para qualquer aplicativo Xamarin.iOS que precise ajustar os valores de um contato existente (ou criar novos), use a NSMutableContact versão da classe e suas subclasses (como CNMutablePostalAddress).

Por exemplo, o código a seguir criará um novo contato e o adicionará à coleção de contatos do usuário:

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

Se esse código for executado em um dispositivo iOS 9, um novo contato será adicionado à coleção do usuário. Por exemplo:

Um novo contato adicionado à coleção do usuário

Formatação e localização de contatos

A estrutura Contatos contém vários objetos e métodos que podem ajudá-lo a formatar e localizar conteúdo para exibição para o usuário. Por exemplo, o código a seguir formataria corretamente um nome de contato e um endereço de correspondência para exibição:

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

Para rótulos de propriedade que você exibirá na interface do usuário do seu aplicativo, a estrutura de Contato também tem métodos para localizar essas cadeias de caracteres. Novamente, isso é baseado na localidade atual do dispositivo iOS em que o aplicativo está sendo executado. Por exemplo:

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

Buscando contatos existentes

Usando uma instância da CNContactStore classe, você pode buscar informações de contato do banco de dados de contatos do usuário. O CNContactStore contém todos os métodos necessários para buscar ou atualizar contatos e grupos do banco de dados. Como esses métodos são síncronos, sugere-se executá-los em um thread em segundo plano para evitar o bloqueio da interface do usuário.

Usando predicados (criados a CNContact partir da classe), você pode filtrar os resultados retornados ao buscar contatos do banco de dados. Para buscar somente contatos que contêm a cadeia de caracteres Appleseed, use o seguinte código:

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

Importante

Predicados genéricos e compostos não são suportados pela estrutura Contatos.

Por exemplo, para limitar a busca apenas às propriedades GivenName e FamilyName do contato, use o seguinte código:

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

Finalmente, para pesquisar o banco de dados e retornar os resultados, use o seguinte código:

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

Se esse código fosse executado após o exemplo que criamos na seção Objetos de contatos acima, ele retornaria o contato "John Appleseed" que acabamos de criar.

Contato Acesso Privacidade

Como os usuários finais podem conceder ou negar acesso às suas informações de contato por aplicativo, na primeira vez que você fizer uma chamada para o CNContactStore, será apresentada uma caixa de diálogo solicitando que eles permitam o acesso ao seu aplicativo.

A solicitação de permissão será apresentada apenas uma vez, na primeira vez que o aplicativo for executado, e as execuções ou chamadas subsequentes para o CNContactStore usarão a permissão que o usuário selecionou naquele momento.

Você deve projetar seu aplicativo para que ele lide normalmente com o usuário que nega acesso ao banco de dados de contatos.

Buscando contatos parciais

Um Contato Parcial é um contato para o qual apenas algumas das propriedades disponíveis foram buscadas no repositório de contatos. Se você tentar acessar uma propriedade que não foi obtida anteriormente, isso resultará em uma exceção.

Você pode verificar facilmente se um determinado contato tem a propriedade desejada usando os IsKeyAvailable métodos ou AreKeysAvailable da CNContact instância. Por exemplo:

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

Importante

Os GetUnifiedContact métodos e GetUnifiedContacts da CNContactStore classe retornam apenas um Contato Parcial limitado às propriedades solicitadas das chaves de busca fornecidas.

Contatos unificados

Um usuário pode ter diferentes fontes de informações de contato para uma única pessoa em seu banco de dados de contatos (como iCloud, Facebook ou Google Mail). Nos aplicativos iOS e OS X, essas informações de contato serão automaticamente vinculadas e exibidas ao usuário como um único Contato Unificado:

Visão geral dos Contatos Unificados

Esse Contato Unificado é uma exibição temporária na memória das informações de contato do link que receberão seu próprio identificador exclusivo (que deve ser usado para buscar novamente o contato, se necessário). Por padrão, a estrutura Contatos retornará um Contato Unificado sempre que possível.

Criando e atualizando contatos

Como vimos na seção Objetos de contato acima, você usa uma CNContactStore e uma instância de a CNMutableContact para criar novos contatos que são gravados no banco de dados de contatos do usuário usando um 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);
}

A CNSaveRequest também pode ser usado para armazenar em cache várias alterações de contato e grupo em uma única operação e agrupar em lote essas modificações no CNContactStore.

Para atualizar um contato não mutável obtido de uma operação de busca, você deve primeiro solicitar uma cópia mutável que, em seguida, modifique e salve novamente no repositório de contatos. Por exemplo:

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

Notificações de alteração de contato

Sempre que um contato é modificado, o Repositório de Contatos posta um CNContactStoreDidChangeNotification na Central de Notificações Padrão. Se você tiver armazenado em cache ou estiver exibindo contatos no momento, será necessário atualizar esses objetos no Repositório de Contatos (CNContactStore).

Contêineres e Grupos

Os contatos de um usuário podem existir localmente no dispositivo do usuário ou como contatos sincronizados com o dispositivo a partir de uma ou mais contas de servidor (como Facebook ou Google). Cada pool de contatos tem seu próprio Container e um determinado Contact só pode existir em um container.

Visão geral de contêineres e grupos

Alguns Contêineres permitem que os Contatos sejam organizados em um ou mais Grupos ou Subgrupos. Esse comportamento depende do armazenamento de backup para um determinado contêiner. Por exemplo, o iCloud tem apenas um Contêiner, mas pode ter muitos Grupos (mas nenhum Subgrupo). O Microsoft Exchange, por outro lado, não oferece suporte a grupos, mas pode ter vários contêineres (um para cada pasta do Exchange).

Sobreposição em contêineres e grupos

O ContactsUI Framework

Para situações em que seu aplicativo não precisa apresentar uma interface do usuário personalizada, você pode usar a estrutura ContactsUI para apresentar elementos de interface do usuário para exibir, editar, selecionar e criar contatos em seu aplicativo Xamarin.iOS.

Ao usar os controles integrados da Apple, você não apenas reduz a quantidade de código que precisa criar para oferecer suporte aos Contatos em seu aplicativo Xamarin.iOS, mas também apresenta uma interface consistente aos usuários do aplicativo.

O controlador de exibição do seletor de contatos

O Controlador de Exibição do Seletor de Contatos (CNContactPickerViewController) gerencia o Modo de Exibição do Seletor de Contatos padrão que permite ao usuário selecionar uma propriedade de Contato ou Contato no Banco de Dados de Contatos do usuário. O usuário pode selecionar um ou mais contatos (com base em seu uso) e o Controlador de Exibição do Seletor de Contatos não solicita permissão antes de exibir o seletor.

Antes de chamar a CNContactPickerViewController classe, você define quais propriedades o usuário pode selecionar e definir predicados para controlar a exibição e a seleção de Propriedades de Contato.

Use uma instância da classe que herda de CNContactPickerDelegate para responder à interação do usuário com o seletor. Por exemplo:

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

Para permitir que o usuário selecione um endereço de email dos contatos em seu banco de dados, você pode usar o seguinte código:

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

O controlador de exibição de contato

A classe Contact View Controller (CNContactViewController) fornece um controlador para apresentar um Modo de Exibição de Contato padrão ao usuário final. O modo de exibição Contato pode exibir novos contatos Novos, Desconhecidos ou Existentes e o tipo deve ser especificado antes que o modo de exibição seja exibido chamando o construtor estático correto (FromNewContact, FromUnknownContact, FromContact). Por exemplo:

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

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

Resumo

Este artigo deu uma olhada detalhada no trabalho com as estruturas de interface do usuário de contato e contato em um aplicativo Xamarin.iOS. Primeiro, ele abordou os diferentes tipos de objetos que a estrutura de contato fornece e como você os usa para criar contatos novos ou acessar existentes. Ele também examinou a estrutura da interface do usuário de contato para selecionar contatos existentes e exibir informações de contato.