通訊錄開發主要是獲取用戶手機中的聯系人,進而可以在應用中添加好友
一 .如何訪問通訊錄
(1)在iOS9之前,有兩個框架可以訪問用戶的通訊錄
AddressBookUI.framework: 提供了聯系人列表界面,聯系人詳情界面,添加練習人界面等,一般用於選擇聯系人
AddressBook.framework: 純C語言的API,僅僅是獲的聯系人數據,沒有提供UI界面展示,需要自己搭建聯系人展示界面,里面的數據類型大部分基於Core Foundation框架,使用起來極其蛋疼
(2)在iOS9開始,也有兩個框架可以訪問用戶的通訊錄
ContactsUI.framework: 對應AddressBookUI.framework
Contacts.framework: 對應AddressBook.framework
二.代碼演示
(1)AddressBookUI的使用
使用步驟
1)創建選擇聯系人控制器
2)設置代理
3)實現代理方法(在代理方法中拿到用戶選擇的聯系人)
4)彈出控制器
代碼如下:
import UIKit import AddressBookUI class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { // 1.創建聯系人選擇的控制器 let ppnc = ABPeoplePickerNavigationController() // 2.設置代理 ppnc.peoplePickerDelegate = self // 3.彈出控制器 present(ppnc, animated: true, completion: nil) } } extension ViewController : ABPeoplePickerNavigationControllerDelegate { // 用戶選中了某一個聯系人 func peoplePickerNavigationController(_ peoplePicker: ABPeoplePickerNavigationController, didSelectPerson person: ABRecord) { // 1.獲取聯系人的姓名 /* Unmanaged<CFTypeRef>? : 非托管對象 * 在Swift和C語言進行混編的過程中,產生的一個臨時對象,真正使用的時候需要將非托管對象,轉成真正的對象才能進行使用 * takeUnretainedValue : 表示在轉化的過程中,不會對對象進行一次retain操作 * takeRetainedValue : 表示在轉化的過程中,有對對象進行一次retain操作 注意:一旦使用takeRetainedValue,那么必須對之前的非托管對象進行一次release(),否則就會產生內存泄漏 let UnManageObjc = ABRecordCopyValue(person, kABPersonLastNameProperty) let lastname = UnManageObjc?.takeRetainedValue() as? String UnManageObjc?.release() */ guard let lastname = ABRecordCopyValue(person, kABPersonLastNameProperty).takeUnretainedValue() as? String else { return } guard let firstname = ABRecordCopyValue(person, kABPersonFirstNameProperty).takeUnretainedValue() as? String else { return } print("姓名:\(firstname) \(lastname)") // 2.獲取聯系人的電話號碼 // ABMultiValue 類似於一個字典,里面有key/value // 2.1.從person中拷貝出來所有的電話號碼 let phones = ABRecordCopyValue(person, kABPersonPhoneProperty).takeUnretainedValue() as ABMultiValue // 2.2.遍歷ABMultiValue中的所有電話 // guard let count = phones.count else { return } 錯誤寫法 let count = ABMultiValueGetCount(phones) for i in 0..<count { let phoneLabel = ABMultiValueCopyLabelAtIndex(phones, i).takeUnretainedValue() as String guard let phoneValue = ABMultiValueCopyValueAtIndex(phones, i).takeUnretainedValue() as? String else { continue } print("phoneLabel:\(phoneLabel) phoneValue:\(phoneValue)") } } }
注意: 這里有一種對象 :Unmanaged<CFTypeRef>? : 非托管對象,這種對象是在Swift和C語言進行混編的過程中,出現的.需要用takeUnretainedValue() 或者 takeRetainedValue()進行轉化.
運行結果如下圖:
(2)AddressBook的使用
1)獲取用戶的授權
獲取授權狀態
如果用戶是未決定狀態,則請求授權
2)獲取聯系人信息
獲取授權狀態
如果是已經授權,則獲取聯系人信息
創建通訊錄對象
獲取通信錄中所有的聯系人
遍歷所有的聯系人,獲取聯系人信息
獲取用戶授權的代碼實現,通常在應用啟動時就詢問用戶授權
AppDelegate中代碼如下所示:
import UIKit import AddressBook @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // 1.獲取用戶的授權狀態 let status = ABAddressBookGetAuthorizationStatus() // 2.判斷授權狀態是否未決定 if status == .notDetermined { // 2.1.創建通信錄對象 let addressBook = ABAddressBookCreate().takeUnretainedValue() // 2.2.請求授權 ABAddressBookRequestAccessWithCompletion(addressBook, { (isFlag : Bool, error : CFError?) in if isFlag { print("授權成功") } else { print("授權失敗") } }) } return true } }
ViewController中代碼如下所示:
import UIKit import AddressBook class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { // 1.獲取用戶授權狀態 let status = ABAddressBookGetAuthorizationStatus() // 2.判斷是否是已經授權 guard status == .authorized else { return } // 3.創建通信錄對象 let addressBook = ABAddressBookCreate().takeUnretainedValue() // 4.從對象中,拷貝出來所有的聯系人 let peopleArray = ABAddressBookCopyArrayOfAllPeople(addressBook).takeUnretainedValue() // 5.遍歷數組,獲取每一個聯系人 let count = CFArrayGetCount(peopleArray) for i in 0..<count { // 5.1.獲取指針 let pointer = CFArrayGetValueAtIndex(peopleArray, i) // 5.2.獲取指針指向的對象 // unsafeBitCast : 將指針轉成某一個對象 let person = unsafeBitCast(pointer, to: ABRecord.self) // 5.3.獲取該聯系人的姓名 guard let lastname = ABRecordCopyValue(person, kABPersonLastNameProperty).takeUnretainedValue() as? String else { continue } guard let firstname = ABRecordCopyValue(person, kABPersonFirstNameProperty).takeUnretainedValue() as? String else { continue } print("姓名:\(firstname) \(lastname)") // 5.4.獲取電話號碼 let phones = ABRecordCopyValue(person, kABPersonPhoneProperty).takeUnretainedValue() as ABMultiValue let phoneCount = ABMultiValueGetCount(phones) for i in 0..<phoneCount { // let phoneLabel = ABMultiValueCopyLabelAtIndex(phones, i).takeUnretainedValue() as String guard let phoneValue = ABMultiValueCopyValueAtIndex(phones, i).takeUnretainedValue() as? String else { continue } print(phoneValue) } } } }
注意: 由於需要訪問請求授權,需要在info.plist配置NSContactsUsageDescription這個key
(3)ContactsUI的使用
1)使用步驟
創建選擇聯系人控制器
設置代理
實現代理方法(在代理中拿到用戶選擇的聯系人)
彈出控制器
2)代碼實現
import UIKit import ContactsUI class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { // 1.創建聯系人選擇的控制器 let cpvc = CNContactPickerViewController() // 2.設置代理 cpvc.delegate = self // 3.彈出控制器 present(cpvc, animated: true, completion: nil) } } extension ViewController : CNContactPickerDelegate { func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) { // 1.獲取用戶的姓名 // lastname --> familyName // firstname --> givenname let lastname = contact.familyName let firstname = contact.givenName print("姓名:\(firstname) \(lastname)") // 2.獲取用戶電話號碼(ABMultivalue) let phones = contact.phoneNumbers for phone in phones { let phoneLabel = phone.label let phoneValue = phone.value.stringValue print("phoneLabel:\(phoneLabel). phoneValue:\(phoneValue)") } } }
輸出結果和AddressBookUI.framework中的輸出結果類似
4)Contacts
1)獲取用戶的授權
獲取授權狀態
如果用戶是未決定狀態,則請求授權
2)獲取聯系人信息
獲取授權狀態
如果是已經授權,則獲取聯系人信息
創建通訊錄對象
獲取通信錄中所有的聯系人
遍歷所有的聯系人,獲取聯系人信息
獲取用戶授權的代碼實現,通常在應用啟動時就詢問用戶授權,請求授權需要在info.plist配置NSContactsUsageDescription這個key
AppDelegate代碼如下所示:
import UIKit import Contacts @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // 1.獲取授權狀態 // CNContactStore --> 通信錄對象 let status = CNContactStore.authorizationStatus(for: .contacts) // 2.判斷如果是未決定狀態,請求授權 if status == .notDetermined { // 2.1.創建通信錄對象 let store = CNContactStore() // 2.2.請求授權 store.requestAccess(for: .contacts, completionHandler: { (isFlag : Bool, error : Error?) in if isFlag { print("授權成功") } else { print("授權失敗") } }) } return true } }
ViewController中代碼如下:
import UIKit import Contacts class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { // 1.獲取授權狀態 let status = CNContactStore.authorizationStatus(for: .contacts) // 2.判斷是否是已經授權 guard status == .authorized else { return } // 3.創建通信錄對象 let store = CNContactStore() // 4.從通信錄中獲取所有的聯系人 // 4.1.獲取fetch,並且指定之后要獲取聯系人中的什么屬性 let keys = [CNContactFamilyNameKey as NSString, CNContactGivenNameKey as NSString, CNContactPhoneNumbersKey as NSString] // 4.2.創建請求對象 let request = CNContactFetchRequest(keysToFetch: keys) // 4.3.遍歷所有的聯系人 do { try store.enumerateContacts(with: request, usingBlock: { (contact : CNContact, stop : UnsafeMutablePointer<ObjCBool>) -> Void in // 1.獲取姓名 let lastname = contact.familyName let firstname = contact.givenName print(lastname, firstname) // 2.獲取電話號碼 let phoneNumers = contact.phoneNumbers for phone in phoneNumers { print(phone.label ?? "沒有Label") print(phone.value.stringValue) } }) } catch { print(error) } } }