iOS通訊錄整合,相容iOS789寫法,附demo,iosios789
蘋果的通訊錄功能在iOS7,iOS8,iOS9 都有著一定的不同,iOS7和8用的是 <AddressBookUI/AddressBookUI.h> ,但是兩個系統版本的代理方法有一些變化,有些代理方法都標註了 NS_DEPRECATED_IOS(2_0, 8_0) 並推薦了另一個代理方法與之對應。 而iOS8到iOS9則是直接棄用了<AddressBookUI/AddressBookUI.h>取而代之的是<ContactsUI/ContactsUI.h>,後者是OC調用,據說當時蘋果宣布棄用AddressBookUI還引來了陣陣歡呼。這也就是在使用通訊錄功能時得考慮版本各種判斷,我也就是工作中遇到了這種坑,然後就順手相容封裝了一下。希望能解決這個問題。
我覺得通訊錄這裡的類結構沒必要像SDWebImage或是Core Location這樣列出來詳細去說。大家用到通訊錄無外乎就三個功能:
1.點擊彈出通訊錄頁面,選擇了一個連絡人的電話後直接將資訊填到頁面輸入框內。
2.遍曆所有的通訊錄資料統一做大量操作,搭建新頁面或直接上傳。
3.給通訊錄寫入一條資訊。
這裡會先對比一下iOS789的寫法,最後奉上demo(一個封裝後的庫,提供了非常便利的api)。不關心內部實現的朋友可以直接拉到demo部分。
一、首先是擷取通訊錄的許可權
iOS7和8保持一致
ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus(); ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL); if (status == kABAuthorizationStatusNotDetermined) { NSLog(@"還沒問"); ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error){ if(granted){ NSLog(@"點擊同意"); }else{ NSLog(@"點擊拒絕"); } }); }else if (status == kABAuthorizationStatusAuthorized){ NSLog(@"已經授權"); [self loadPerson]; }else { NSLog(@"沒有授權"); // 彈窗提示去擷取許可權 }
iOS9及以後調用方法改成
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts]; if (status == CNAuthorizationStatusNotDetermined) { [[[CNContactStore alloc]init] requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) { NSLog(@"還沒問"); if(granted){ NSLog(@"點擊了同意"); [self loadPerson]; }else{ NSLog(@"點擊了拒絕"); } }]; }else if (status == CNAuthorizationStatusAuthorized){ NSLog(@已經授權"); }else { NSLog(@"沒有授權"); }
二、彈出通訊錄選擇介面
iOS7的寫法如下,代理方法的傳回值大多是BOOL類型。
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person{ return YES;}- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{ ABMultiValueRef phone = ABRecordCopyValue(person, kABPersonPhoneProperty); long index = ABMultiValueGetIndexForIdentifier(phone,identifier); NSString *phoneNO = (__bridge NSString *)ABMultiValueCopyValueAtIndex(phone, index); CFStringRef lastName = ABRecordCopyValue(person, kABPersonLastNameProperty); CFStringRef firstName = ABRecordCopyValue(person, kABPersonFirstNameProperty); NSString *lastname = (__bridge_transfer NSString *)(lastName); NSString *firstname = (__bridge_transfer NSString *)(firstName); if (phone) { [peoplePicker dismissViewControllerAnimated:YES completion:nil]; return NO; } return YES;}
iOS8的代理方法換了,改成了下面兩個,但是方法內部的取值基本相同
// 點擊了通訊錄名字就會退出- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person;// 點擊了名字裡面的電話或郵箱才會退出- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier;
至於會調用哪一個方法,可以根據實際需要去選擇,在彈出介面的方法中predicateForSelectionOfPerson 這個屬性傳false就是調用下面的。
ABPeoplePickerNavigationController *pickervc = [[ABPeoplePickerNavigationController alloc] init]; pickervc.predicateForSelectionOfPerson = [NSPredicate predicateWithValue:false]; pickervc.peoplePickerDelegate = self; [target presentViewController:pickervc animated:YES completion:nil];
iOS9系統下的彈出選取器方法 和 代理方法如下
// 彈出選取器 - (void)presentPageOnTarget{ CNContactPickerViewController *contactVc = [[CNContactPickerViewController alloc] init]; contactVc.delegate = self; [target presentViewController:contactVc animated:YES completion:nil];}// 代理方法- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact{ SXPersonInfoEntity *personEntity = [SXPersonInfoEntity new]; NSString *lastname = contact.familyName; NSString *firstname = contact.givenName; NSLog(@"%@ %@", lastname, firstname); personEntity.lastname = lastname; personEntity.firstname = firstname; NSMutableString *fullname = [[NSString stringWithFormat:@"%@%@",lastname,firstname] mutableCopy]; [fullname replaceOccurrencesOfString:@"(null)" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, fullname.length)]; personEntity.fullname = fullname; NSString *fullPhoneStr = [NSString string]; NSArray *phoneNums = contact.phoneNumbers; for (CNLabeledValue *labeledValue in phoneNums) { NSString *phoneLabel = labeledValue.label; CNPhoneNumber *phoneNumer = labeledValue.value; NSString *phoneValue = phoneNumer.stringValue; NSLog(@"%@ %@", phoneLabel, phoneValue); if (phoneValue.length > 0) { fullPhoneStr = [fullPhoneStr stringByAppendingString:phoneValue]; fullPhoneStr = [fullPhoneStr stringByAppendingString:@","]; } } if (fullPhoneStr.length > 1) { personEntity.phoneNumber = [fullPhoneStr substringToIndex:fullPhoneStr.length - 1]; } self.chooseAction(personEntity);}
這個是點擊了名字就直接回調的方法,如果希望點擊了屬性再回調,則需要加上這一行
contactVc.predicateForSelectionOfContact = [NSPredicate predicateWithValue:false];// 代理方法調用- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty
三、擷取全部通訊錄資訊
關於批量擷取所有通訊錄資訊的方法有點冗長,這裡就不一一貼了,只貼下iOS9的寫法,iOS7和8的代碼demo裡都有。
- (void)printAllPerson{ // 擷取 CNContactStore *contactStore = [[CNContactStore alloc] init]; NSArray *keys = @[CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey]; CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:keys]; // 遍曆 [contactStore enumerateContactsWithFetchRequest:request error:nil usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) { NSString *lastname = contact.familyName; NSString *firstname = contact.givenName; NSLog(@"%@ %@", lastname, firstname); NSArray *phoneNums = contact.phoneNumbers; for (CNLabeledValue *labeledValue in phoneNums) { NSString *phoneLabel = labeledValue.label; CNPhoneNumber *phoneNumer = labeledValue.value; NSString *phoneValue = phoneNumer.stringValue; NSLog(@"%@ %@", phoneLabel, phoneValue); } }];}
四、寫入通訊錄
因為寫入的話這個功能有點重量級,寫入的時候要寫入,名字、電話、email、地址等等,這就會使得api過於複雜。暫時我見到過的做法大多都是如果使用者給了通訊錄許可權 那就給你插入一條名字+電話,我做了只有這兩個入參的api,當然使用時也完全可以擴充成更多參數的。
iOS7和8
- (void)creatItemWithName:(NSString *)name phone:(NSString *)phone{ if((name.length < 1)||(phone.length < 1)){ NSLog(@"輸入屬性不可為空"); return; } CFErrorRef error = NULL; ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error); ABRecordRef newRecord = ABPersonCreate(); ABRecordSetValue(newRecord, kABPersonFirstNameProperty, (__bridge CFTypeRef)name, &error); ABMutableMultiValueRef multi = ABMultiValueCreateMutable(kABMultiStringPropertyType); ABMultiValueAddValueAndLabel(multi, (__bridge CFTypeRef)name, kABPersonPhoneMobileLabel, NULL); ABRecordSetValue(newRecord, kABPersonPhoneProperty, multi, &error); CFRelease(multi); ABAddressBookAddRecord(addressBook, newRecord, &error); ABAddressBookSave(addressBook, &error); CFRelease(newRecord); CFRelease(addressBook);}
iOS9下
- (void)creatItemWithName:(NSString *)name phone:(NSString *)phone{ // 建立對象 // 這個裡面可以添加多個電話,email,地址等等。 感覺使用率不高,只提供了最常用的屬性:姓名+電話,需要時可以自行擴充。 CNMutableContact * contact = [[CNMutableContact alloc]init]; contact.givenName = name?:@"defaultname"; CNLabeledValue *phoneNumber = [CNLabeledValue labeledValueWithLabel:CNLabelPhoneNumberMobile value:[CNPhoneNumber phoneNumberWithStringValue:phone?:@"10086"]]; contact.phoneNumbers = @[phoneNumber]; // 把對象加到請求中 CNSaveRequest * saveRequest = [[CNSaveRequest alloc]init]; [saveRequest addContact:contact toContainerWithIdentifier:nil]; // 執行請求 CNContactStore * store = [[CNContactStore alloc]init]; [store executeSaveRequest:saveRequest error:nil];}
五、我的demo
因為不同版本用的類和枚舉都不一樣,所以我要設定一個統一的,並且在我的manager中處理各個版本間的判斷。 最後開放出來統一的api,只要引入標頭檔SXAddressBookManager.h 就可以使用這些通用介面了。
①檢查目前狀態,有兩種api
- (void)checkStatus1{ SXAddressBookAuthStatus status = [[SXAddressBookManager manager]getAuthStatus]; if (status == kSXAddressBookAuthStatusNotDetermined) { [[SXAddressBookManager manager]askUserWithSuccess:^{ NSLog(@"點擊同意"); } failure:^{ NSLog(@"點擊拒絕"); }]; }else if (status == kSXAddressBookAuthStatusAuthorized){ NSLog(@"已有許可權"); }else{ NSLog(@"沒有許可權"); }}
- (void)checkStatus2{ [[SXAddressBookManager manager]checkStatusAndDoSomethingSuccess:^{ NSLog(@"已經有許可權,做相關操作,可以做讀取通訊錄等操作"); } failure:^{ NSLog(@"未得到許可權,做相關操作,可以做彈窗詢問等操作"); }];}
②彈出選擇視窗,點擊回調選中的資訊
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ [[SXAddressBookManager manager]presentPageOnTarget:self chooseAction:^(SXPersonInfoEntity *person) { NSLog(@"%@---%@",person.fullname,person.phoneNumber); }];}
③獲得整個通訊錄資訊
self.personEntityArray = [[SXAddressBookManager manager]getPersonInfoArray];
④往通訊錄寫入一條資訊
[[SXAddressBookManager manager]creatItemWithName:@"雷克薩斯-北京諮詢電話" phone:@"010-88657869"];
demo的地址是
https://github.com/dsxNiubility/SXEasyAddressBook
這裡寫了我說的那三點常用,如果以後有一些剛需,會不斷補充。 董鉑然部落格園。