本文翻譯自Chris Grant的《iOS9 Day-by-Day :: Day 7 :: Contacts Framework》(https://www.shinobicontrols.com/blog/ios9-day-by-day-day7-contacts-framework)佩伤。感謝Chris Grant的辛苦工作沿猜!
iOS 9引入了一個新的聯(lián)系人框架(Contact)。通過這個框架膘盖,我們可以使用Objective-C或者Swift API訪問設(shè)備的通訊錄。這比之前通過AddressBook
框架進行訪問改進了不少斤蔓。AddressBook
框架沒有提供Objective-C接口孵滞,引起使用起來非常復(fù)雜。而對于Swift用戶來說遏餐,這真是解決了一大痛點。
WWDC上宣布在iOS 9里廢棄AddressBook
后的歡呼聲說明了開發(fā)者有多么痛恨這個框架赢底。
使用該框架所獲取的聯(lián)系人都是唯一的失都,因此我們不需要擔心從不同來源復(fù)制數(shù)據(jù)的時候,會產(chǎn)生重復(fù)內(nèi)容幸冻。事實上粹庞,它們會被合并在一起再呈現(xiàn)給我們。因此并不需要我們手動的合并聯(lián)系人信息嘁扼。
使用新的聯(lián)系人框架
下面我們將構(gòu)建一個簡單的應(yīng)用程序來展示通訊錄信粮,并且允許查看聯(lián)系人詳情。

可以看到趁啸,我們使用的是Master-Detail應(yīng)用程序强缘,它同樣可以在iPhone上運行督惰。左側(cè)顯示通訊錄,而右側(cè)顯示選中的聯(lián)系人的圖片旅掂、姓名赏胚、電話等詳細信息。
獲取用戶的通訊錄
使用Xcode的Master-Detail模板創(chuàng)建一個應(yīng)用程序商虐。打開MasterViewController
觉阅,用import
引入Contacts
和ContactsUI
框架。
import Contacts
import ContactsUI
替換默認的數(shù)據(jù)源行為秘车,其中一個用于獲取數(shù)據(jù)典勇,另外一個用于顯示當前聯(lián)系人信息。
func findContacts() -> [CNContact] {
let store = CNContactStore()
}
CNContactStore
可以用于獲取和保存聯(lián)系人叮趴。雖然它可以被用來存取聯(lián)系人組割笙,但本文只會使用它來訪問聯(lián)系人信息。
let keysToFetch = [CNContactFormatter.descriptorForRequredKeysForStyle(.FullName), CNContactImageDataKey, CNContactPhoneNumbersKey]
let fetchRequest = CNContactFetchRequest(keysToFetch: keysToFetch)
一旦獲取了存儲對象(Store)眯亦,就可以通過請求對象(Fetch Request)進行查詢伤溉。創(chuàng)建CNContactFetchRequest
需要通過數(shù)組說明請求的字段。其中有意思的一個是CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName)
妻率。它是CNContactFormatter
的一個快捷方法乱顾。如果沒有這個方法的話,CNContactFormatter
需要手動指定多個不同的Key:
[CNContactGivenNameKey,
CNContactNamePrefixKey,
CNContactNameSuffixKey,
CNContactMiddleNameKey,
CNContactFamilyNameKey,
CNContactTypeKey...]
不但需要代碼量較多宫静,而且以后有可能發(fā)生變化走净。
var contacts = [CNContact]()
do {
try store.enumerateContactsWithFetchRequest(fetchRequest, usingBlock: {
(let contact, let stop) -> Void in
contacts.append(contact)
})
}
catch let error as NSError {
print(error.localizedDescription)
}
return contacts
可以看出,通過CNContactStore
獲取通訊錄的代碼非常簡單囊嘉。這個請求對象并沒有明確過濾條件温技,因此會返回所有的聯(lián)系人革为。
接下來就只要在MasterViewController
中增加一個屬性來保存所獲取到的信息扭粱。
var contacts = [CNContact]()
然后在viewDidLoad
中更新UITableView
(異步)。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
self.contacts = self.findContacts()
dispatch_async(dispatch_get_main_queue()) {
self.tableView!.reloadData()
}
}
需要對UITableViewDataSource
的幾個方法進行修改:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.contacts.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let contact = contacts[indexPath.row] as CNContact
cell.textLabel!.text = "\(contact.givenName) \(contact.familyName)"
return cell
}
接下來就是修改DetailViewController
來顯示聯(lián)系人詳情震檩。這里我們不會深入討論怎么實現(xiàn)琢蛤,而只在界面上添加一個圖片視圖和兩個分別顯示姓名和電話號碼的標簽,并與DetailViewController
建立IBOutlet
連接抛虏。
@IBOutlet weak var contactImageView: UIImageView!
@IBOutlet weak var contactNameLabel: UILabel!
@IBOutlet weak var contactPhoneNumberLabel: UILabel!
一旦完成UI的設(shè)計博其,我們就可以給它們設(shè)置正確的值。下面是CNContact
對象的使用方法迂猴。在configureView
方法中添加以下代碼:
label.text = CNContactFormatter.stringFromContact(contact, style: .FullName)
根據(jù)上面的討論慕淡,CNContactFormatter
可以獲取指定格式的聯(lián)系人姓名字符串。在顯示頭像之前沸毁,需要檢查是否設(shè)置了頭像峰髓。imageData
是Optional類型的變量傻寂,如果沒有設(shè)置頭像,可能會導(dǎo)致程序崩潰携兵。
if contact.imageData != nil {
imageView.image = UIImage(data: contact.imageData!)
}
else {
imageView.image = nil
}
如果存在頭像疾掰,使用它創(chuàng)建一個UIImage
對象,并顯示在UIImageView
上徐紧。最后就是顯示電話號碼的代碼:
if let phoneNumberLabel = self.contactPhoneNumberLabel {
var numberArray = [String]()
for number in contact.phoneNumbers {
let phoneNumber = number.value as! CNPhoneNumber
numberArray.append(phoneNumber.stringValue)
}
phoneNumberLabel.text = ", ".join(numberArray)
}

使用ContactsUI
選取聯(lián)系人
如果我們希望用戶可以選取聯(lián)系人并在程序中使用静檬。上面的做法需要編寫大量代碼來手動獲取信息和顯示UI。實際上還有一種更簡單的方式來做這件事情并级。iOS 9給我們提供了ContactsUI
框架拂檩。它有一組可以顯示聯(lián)系人信息的視圖控制器。
在這一節(jié)中嘲碧,我們想要用戶能夠選取一個電話號碼广恢,并記錄在程序中。由于這只是一個簡單的示例呀潭,我們在MasterViewController
的導(dǎo)航條右側(cè)增加一個UIBarButtonItem
作為入口钉迷。這個按鈕關(guān)聯(lián)的MasterViewController
中方法如下:
@IBAction func showContactsPicker(sender: UIBarButtonItem) {
let contactPicker = CNContactPickerViewController()
contactPicker.delegate = self;
contactPicker.displayedPropertyKeys = [CNContactPhoneNumbersKey]
self.presentViewController(contactPicker, animated: true, completion: nil)
}
我們創(chuàng)建了一個新的CNContactPickerViewController
并設(shè)置代理。這樣我們就可以獲取用戶響應(yīng)钠署,并指定只獲取電話號碼糠聪,然后顯示視圖控制器。
func contactPicker(picker: CNContactPickerViewController, didSelectContactProperty contactProperty: CNContactProperty) {
let contact = contactProperty.contact
let phoneNumber = contactProperty.value as! CNPhoneNumber
print(contact.givenName)
print(phoneNumber.stringValue)
}
在contactPicker
的代理方法didSelectContactProperty
中谐鼎,我們得到一個CNContactProperty
對象舰蟆。它封裝了一個CNContact
對象以及感興趣的屬性。

點擊MasterViewController
右上角的UIBarButtonItem
后狸棍,就會顯示上圖的界面身害。由于我們沒有給CNContactPickerViewController
添加過濾器,因此它會列出所有的聯(lián)系人草戈。

一旦選取一個聯(lián)系人塌鸯,就會得到他的所有電話號碼,但是并不會顯示其它信息唐片。最后丙猬,如果選中其中一個屬性,比如上圖中的電話號碼费韭,就會調(diào)用contactPicker:didSelectContactProperty
并回到上一個頁面茧球。
這樣我們就拿到了對應(yīng)聯(lián)系人“Kate Bell”的CNContact
對象以及“phoneNumber”,其中“5555648583”是電話號碼星持。
總而言之抢埋,使用ContactsUI
框架來選取聯(lián)系人信息非常簡單。我們我們需要更靈活的操作聯(lián)系人,就需要使用Contacts
框架的功能揪垄。
更多信息
請觀看WWDC session 223:“Introducing the Contacts Framework for iOS and OS X”鲤屡。別忘了我們的示例代碼在GitHub上。
戴維營教育
戴維營教育(Dive In Education)福侈,潛心做IT職業(yè)教育酒来!緊跟時代潮流,不弄虛作假肪凛!不忘初心堰汉!
- 官網(wǎng):戴維營教育http://www.diveinedu.com
- 在線視頻:戴維營學(xué)院http://v.diveinedu.com
- 問答網(wǎng):潛心俱樂部http://divein.club