在之前開發者訪問用戶的聯繫人在他們的iOS設備上使用C API,那是個蠻痛苦的事情。直到iOS8還一直在使用,不過如今Apple推出了兩個功能強大的面相對象的框架來管理用戶聯繫人。Contacts
和ContactsUI
接下來,咱們就來看看如何使用這些框架。git
博客原文swift
初始項目數組
使用ContactsUI
框架來顯示和選擇聯繫人。安全
將聯繫人添加到用戶的聯繫人列表。閉包
搜索用戶的聯繫人,並使用NSPredicate
篩選。app
運行你下載的初始化項目,你將看到一個聯繫人的列表。框架
CNContact
這個類中包含了不少的聯繫人的屬性,好比說,givenName
,familyName
,emailAddress
,imageData
等等。。。async
打開咱們項目中的Friend.swift
添加下邊代碼:ide
import Contacts
函數
如今呢,擴展這個類,添加一個計算屬性
extension Friend{ var contactValue:CNContact{ //1 建立一個不帶參數的 `CNMutable` 聯繫實例 let contact = CNMutableContact() //2更新對應的屬性 contact.givenName = firstName contact.familyName = lastName //3 emailAddresses 是 CNLabeledValue 對象的數組。這意味着每一個電子郵件地址都有一個對應的標籤。有多種類型可供接觸的標籤,但在這種狀況下,你堅持使用CNLabelWork。 contact.emailAddress = [CNLabeledValue(label:CNLabelWork,value:workEmail)] //4若是有朋友的圖片,聯繫人的圖像數據用JPEG格式 if let profilePicture = profilePicture{ let imageData = UIImageJPEGRepresentation(profilePicture, 1) contact.imageData = imageData } //5返回複製的副本 return contact.copy() as! CNContact } }
提示:
CNMutableContact
是CNContact
的可變類型,CNContact
的屬性只能夠讀,而CNMutableContact
的屬性能夠改變。所以,你建立一個可變的聯繫人,設置其屬性,而後完成之後,返回一個不可變的副本。須要注意的是CNContacat
是線程安全的,而CNMutableContact
不是。
在FriendsViewController.swift
添加下邊的代碼:
import Contacts
import COntactsUI
extension FriendsViewController{ override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { tableView.deselectRowAtIndexPath(indexPath, animated: true) //1 使用所選單元格的索引路徑來得到所選擇的朋友,並將其轉換到CNContact的一個實例 let friend = friendsList[indexPath.row] let contact = friend.contactValue //2使用forUnknownContact來初始化一個`CNContactViewController`,由於聯繫人不在用戶的聯繫人存儲中。你還能夠自定義導航欄和標籤欄的屬性。 let contactViewController = CNContactViewController(forUnknownContact: contact) contactViewController.navigationItem.title = "Profile" contactViewController.hidesBottomBarWhenPushed = true //3設置這兩個屬性爲false,那麼用戶智能查看聯繫人的信息 contactViewController.allowsActions = false contactViewController.allowsEditing = false //4 navigationController?.pushViewController(contactViewController, animated: true) } }
如今你運行你的應用程序,點擊一個 tableView cell 你將看到ContactsUI framework
將會展示朋友的信息。
你不能添加更多的朋友好友列表?您可使用ContactsUI類CNContactPickerViewController讓你的用戶選擇聯繫人的應用程序來使用。
帶開Main.storyboard
給friendsViewController
添加一個Bar Button Item
在導航欄的右側,而且設置他的image
屬性爲AddButton
。
接着給他鏈接一個方法addFriends
在這個方法中寫:
@IBAction func addFriends(sender: UIBarButtonItem) { let contactPicker = CNContactPickerViewController() presentViewController(contactPicker, animated: true, completion: nil) }
而後你運行你的程序,點擊按鈕,就會看到下邊的樣子:
目前,用戶沒法導入聯繫人。當用戶選擇一個聯繫人,選擇器視圖控制器只顯示有關聯繫人的詳細信息。爲了解決這個問題,你須要採起的CNContactPickerDelegate方法的優點。
CNContactPickerDelegate
有五個可選的方法,如今你可能有興趣的是contactPicker(:didSelectContacts)
。
extension FriendsViewController:CNContactPickerDelegate{ func contactPicker(picker: CNContactPickerViewController, didSelectContacts contacts: [CNContact]) { } }
如今FriendsViewController
遵循 CNContactPickerDelegate
這個協議,你已經有了一個空的contactPicker(_:didSelectContacts:)
方法,接下來要作的事情有:
從CNContscts建立一個新的朋友實例
增長新的朋友實例給你的朋友列表
打開Friend.swift
添加以下的初始化方法
extension Friend{ init(contact:CNContact){ firstName = contact.givenName lastName = contact.familyName workEmail = contact.emailAddresses.first?.value as! String if let imagedata = contact.imageData{ profilePicture = UIImage(data: imagedata) }else{ profilePicture = nil } } }
當您設置workEmail強制解開找到的第一個電子郵件地址。因爲RWConnect使用電子郵件,以保持與您的朋友保持聯繫,全部的聯繫人必須有電子郵件地址。若是強行展開這樣使你焦躁不安,不用擔憂- 你將在之後解決它!
接下來完成你的contactPicker(_:didSelectContacts:)
這個代理方法
extension FriendsViewController:CNContactPickerDelegate{ func contactPicker(picker: CNContactPickerViewController, didSelectContacts contacts: [CNContact]) { let newFriends = contacts.map{Friend(contact: $0)} for friend in newFriends{ if !friendsList.contains(friend){ friendsList.append(friend) } } tableView.reloadData() } }
找到你的addFriends(_:)
方法,在 presentViewController(_:animated:completion:)
以前添加下邊的代碼
contactPicker.delegate = self
如今你來運行你的程序,來添加新的聯繫人到你的程序列表中。
你覺得就這樣完了麼?NONONO還有一個小bug咱們沒有解決,還記得咱們是強制解析 emailAddress
,若是你選的聯繫人沒有郵箱地址的話,這個程序會crash掉。咱們要來解決這個問題。
咱們在這裏解決的辦法是讓用戶不能選擇沒有電子郵件的聯繫人。
在presentViewcontroller(:animated:completion)
以前添加下面的代碼:
contactPicker.predicateForEnablingContact = NSPredicate(format: "emailAddress.@count > 0")
聯繫人選取器的predicateForEnablingContact
屬性可讓你決定哪些聯繫人能夠選擇。
當你再次運行項目的時候,點擊添加按鈕,你會看到,若是沒有電子郵件地址的聯繫人顯示爲灰色。
咱們給單元格添加一個左滑的動做,會顯示獎聯繫人添加到聯繫人存儲。
override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? { let createContact = UITableViewRowAction(style: .Normal, title: "Create Contact") { (rowaction, indexPath) -> Void in //todo: add the contact } createContact.backgroundColor = BlueColor return [createContact] }
在你訪問或者修改用戶的聯繫人,你必須獲得用戶的許可。
在這裏你可能會疑惑,在上邊使用 CNContactPickerViewController
時,你並無索取用戶的許可,這是爲何呢。這是由於CNContactPickerViewController
是一個out-of-process
。當用戶選擇了聯繫人的時候,就暗示了你容許使用他們的聯繫人。
let contactStore = CNContactStore() contactStore.requestAccessForEntityType(CNEntityType.Contacts, completionHandler: { (userFrantedAccess, _) -> Void in })
在這裏你建立了一個CNContactStore
實例,這表明用戶的地址薄中包含全部它們的聯繫人,一旦你初始化聯繫人存儲,調用實例方法 requestAccessForEntityType
完成處理須要一個布爾值,指示用戶授予權限來訪問他們的contacts。
Add the following method to FriendsViewController:
func presentPremissionErrorAlert(){ dispatch_async(dispatch_get_main_queue()) { () -> Void in let alert = UIAlertController(title: "Could Not Save Contact", message: "How an I supposed to add the contact if you didn't give me premission", preferredStyle: UIAlertControllerStyle.Alert) let openSettingsAction = UIAlertAction(title: "Settings", style: .Default, handler: { (alert) -> Void in //打開設置 UIApplication.sharedApplication().openURL(NSURL(string: UIApplicationOpenSettingsURLString)!) }) let dismissAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil) alert.addAction(openSettingsAction) alert.addAction(dismissAction) self.presentViewController(alert, animated: true, completion: nil) } }
上邊的方法提供了一個警告,表示應用程序沒法保存聯繫人用戶。經過UIApplicationOpenSettingsURLString
來打開設置應用程序。
須要注意的是咱們把上邊的這個警告放在了主線程裏來執行,這樣能夠更快的顯示出警告。
咱們完成咱們的requestAccessForEntityType
閉包。
guard userFrantedAccess else{ self.presentPremissionErrorAlert() return }
如今在你的模擬器中來運行項目
若是你選擇了不容許的話將會出現下邊的警告
若是你選擇了設置,那麼會打開設置應用。
咱們來建立一個保存聯繫人的方法。
func saveFriendToContacts(friend:Friend){ //1 首先呢咱們須要一個可變的CNContact let contact = friend.contactValue.mutableCopy() as! CNMutableContact //2 新建一個CNSaveRequest 來更新或者刪除聯繫人CNContactStore let saveRequest = CNSaveRequest() //3 接着告訴CNSaveRequest你想把新的朋友添加到用戶的聯繫人 saveRequest.addContact(contact, toContainerWithIdentifier: nil) do { //4 嘗試着保存要求,若是該方法成功,則繼續執行,你能夠假設保存請求成功,不然拋出一個錯誤 let contactStore = CNContactStore() try contactStore.executeSaveRequest(saveRequest) //show success alert }catch{ //show failure alert } }
接下來完成show success alert
和show failure alert
//show success alert dispatch_async(dispatch_get_main_queue()) { let successAlert = UIAlertController(title: "Contacts Saved", message: "nil", preferredStyle: .Alert) successAlert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil)) self.presentViewController(successAlert, animated: true, completion: nil) }
//show failure alert dispatch_async(dispatch_get_main_queue(), { () -> Void in let failureAlert = UIAlertController(title: "Could Not Save Contact", message: "An Unknown error occurred.", preferredStyle: .Alert) failureAlert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil)) self.presentViewController(failureAlert, animated: true, completion: nil) })
咱們在點擊了cell左滑的按鈕以後來執行這個方法
guard userFrantedAccess else{ self.presentPremissionErrorAlert() return } let friend = self.friendsList[indexPath.row] self.saveFriendToContacts(friend)
咱們在運行項目以前須要重置一下模擬器。
以後從新運行,來保存你的朋友到你通信錄裏
你能夠到你的通信錄裏看到你新添加的朋友
細心的同窗可能會發現,若是你屢次添加一個聯繫人的話也能夠成功。咱們要阻止這樣的狀況發生。
添加下面的代碼到saveFriendToContacts
函數的最上邊
//1 let contactFormatter = CNContactFormatter() //2 格式化基於聯繫人給出的姓名字符串 let contactName = contactFormatter.stringFromContact(friend.contactValue)! //3 用於搜索是否存在上邊的姓名 let predicateForMatchingName = CNContact.predicateForContactsMatchingName(contactName) //4 let matchingContacts = try! CNContactStore().unifiedContactsMatchingPredicate(predicateForMatchingName, keysToFetch: []) //5 只保存沒有匹配到的聯繫人 guard matchingContacts.isEmpty else{ dispatch_async(dispatch_get_main_queue(), { () -> Void in let alert = UIAlertController(title: "Contact Already Exists", message: nil, preferredStyle: .Alert) alert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil)) self.presentViewController(alert, animated: true, completion: nil) }) return }
完結!