Xamarin.iOS 中的聯繫人和聯繫人UI

本文涵蓋在 Xamarin.iOS 應用程式中使用新的聯繫人和連絡人 UI 架構。 這些架構會取代舊版 iOS 中使用的現有通訊錄和通訊簿 UI。

隨著 iOS 9 的推出,Apple 發行了兩個新的架構, ContactsContactsUI取代 iOS 8 和更早版本所使用的現有通訊簿和通訊簿 UI 架構。

這兩個新的架構包含下列功能:

  • 連絡人 - 提供使用者聯絡人列表數據的存取權。 由於大部分的應用程式只需要只讀存取權,因此此架構已針對安全線程、唯讀存取進行優化。

  • ContactsUI - 提供 Xamarin.iOS UI 元素,以在 iOS 裝置上顯示、編輯、選取及建立聯繫人。

An example Contact Sheet on an iOS device

重要

iOS 8 使用的現有 AddressBookAddressBookUI 架構已在 iOS 9 中已被取代,且應儘快取代為任何現有的 Xamarin.iOS 應用程式的新 ContactsContactsUI 架構。 應針對新的架構撰寫新的應用程式。

在下列各節中,我們將探討這些新架構,以及如何在 Xamarin.iOS 應用程式中實作這些架構。

The Contacts Framework

Contacts Framework 提供 Xamarin.iOS 存取使用者的連絡資訊。 由於大部分的應用程式只需要只讀存取權,因此此架構已針對安全線程、唯讀存取進行優化。

聯繫人物件

類別CNContact提供安全線程、只讀存取聯繫人的屬性,例如 Name、Address 或 電話 Numbers。 CNContact 和 之類的 NSDictionary 函式包含多個唯讀屬性集合(例如地址或電話號碼):

Contact Object overview

對於任何可以具有多個值的屬性(例如電子郵件地址或電話號碼),它們都會表示為 對象的陣列 NSLabeledValueNSLabeledValue 是一個線程安全元組,由一組只讀標籤和值組成,其中標籤會定義使用者的值(例如 Home 或 Work 電子郵件)。 Contacts 架構提供預先定義的標籤(透過 CNLabelKeyCNLabelPhoneNumberKey 靜態類別)的選擇,您可以在您的應用程式中使用,或您可以選擇定義自定義標籤以符合您的需求。

對於任何需要調整現有聯繫人值 (或建立新聯繫人) 的 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

連絡人格式設定和當地語系化

Contacts 架構包含數個物件和方法,可協助您格式化和當地語系化內容以向用戶顯示。 例如,下列程式代碼會正確格式化聯繫人名稱和郵件地址來顯示:

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

對於您要在應用程式 UI 中顯示的屬性標籤,Contact Framework 也有用來當地語系化這些字串的方法。 同樣地,這會根據應用程式執行時所執行之 iOS 裝置的目前地區設定。 例如:

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

擷取現有的聯繫人

藉由使用 類別的 CNContactStore 實例,您可以從使用者的聯繫人資料庫擷取聯繫人資訊。 CNContactStore包含從資料庫擷取或更新聯繫人和群組所需的所有方法。 由於這些方法是同步的,因此建議您在背景線程上執行它們,以防止封鎖 UI。

藉由使用述詞(從 CNContact 類別建置),您可以篩選從資料庫擷取聯繫人時傳回的結果。 若要只擷取包含字串的連絡人 Appleseed,請使用下列程式代碼:

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

重要

Contacts 架構不支援泛型和複合述詞。

例如,若要將擷取限制為只有聯繫人的 GivenNameFamilyName 屬性,請使用下列程式代碼:

// 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 將會使用使用者當時選取的許可權。

您應該設計應用程式,使其可正常處理拒絕其聯繫人數據庫存取權的使用者。

擷取部分聯繫人

部分 聯繫人 是聯繫人,只有部分可用的屬性已從聯繫人存放區擷取。 如果您嘗試存取先前未擷取的屬性,則會導致例外狀況。

您可以使用 實例的 CNContactAreKeysAvailable 方法,輕鬆地檢查指定的聯繫人是否具有所需的屬性IsKeyAvailable。 例如:

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

重要

類別GetUnifiedContactCNContactStoreGetUnifiedContacts 方法只會傳回限制為所提供提取密鑰所要求屬性的 Partial Contact。

整合連絡人

使用者對於聯繫人資料庫中的單一人員,可能會有不同的聯繫人資訊來源(例如 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

The ContactsUI Framework

針對應用程式不需要呈現自定義UI的情況,您可以使用 ContactsUI 架構來呈現UI元素,以在 Xamarin.iOS 應用程式中顯示、編輯、選取和建立連絡人。

藉由使用Apple的內建控件,您不僅可減少在 Xamarin.iOS 應用程式中建立以支援聯繫人的程式代碼數量,而且會向應用程式的用戶呈現一致的介面。

聯繫人選擇器檢視控制器

聯繫人選擇器檢視控制器 (CNContactPickerViewController) 會管理標準聯繫人選擇器檢視,可讓使用者從使用者的聯繫人資料庫選取聯繫人或聯繫人屬性。 用戶可以選取一或多個聯繫人(根據其使用方式),而且聯繫人選擇器檢視控制器在顯示選擇器之前未提示許可權。

呼叫 類別 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、 、 、 。 FromUnknownContactFromContact 例如:

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

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

摘要

本文已詳細探討如何在 Xamarin.iOS 應用程式中使用聯繫人和聯繫人 UI 架構。 首先,它涵蓋聯繫人架構所提供的不同類型的物件,以及如何使用它們來建立新的或存取現有的聯繫人。 它也會檢查聯繫人 UI 架構,以選取現有的連絡人並顯示聯絡人資訊。