iOS通訊錄整合,相容iOS789寫法,附demo

来源:http://www.cnblogs.com/dsxniubility/archive/2016/05/31/5541565.html
-Advertisement-
Play Games

蘋果的通訊錄功能在iOS7,iOS8,iOS9 都有著一定的不同,iOS7和8用的是 <AddressBookUI/AddressBookUI.h> ,但是兩個系統版本的代理方法有一些變化,有些代理方法都標註了 NS_DEPRECATED_IOS(2_0, 8_0) 並推薦了另一個代理方法與之對應。 ...


蘋果的通訊錄功能在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

這裡寫了我說的那三點常用,如果以後有一些剛需,會不斷補充。 董鉑然博客園。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. 界面美觀 1.1. 主題 1.2. 改Project面板大小 2. 快捷鍵 2.1智能提示 2.2常用快捷鍵 3.編輯器調整 3.1 代碼提示忽略大小寫 3.2 顯示行號 3.3 優化自動導入 3.3 關閉啟動IDEA自動打開項目 1. 界面美觀 1.1. 主題 1.2. 改Project面板 ...
  • Fragment 裡面 使用輕量級的數據存儲sharepreference ,代碼思路清晰。保存輸入框裡面的數據,實現按鈕保存。 個人項目中簡單清晰代碼: 趙存檔 編寫 ,可以參考: 類繼承Fragment實現點擊事件: 本代碼來源個人博客:http://www.cnblogs.com/xiaobo ...
  • 現在開始寫博客,分享android開發中的心得。 ...
  • 運行結果列印: 原文文檔介紹: Runs the loop once, blocking for input in the specified mode until a given date. //執行loop一次,堵塞等待給定模式的輸入直至給定的時間點 Parameters mode The mo ...
  • 做基於WebView應用時,頁面上有一個輸入框,當輸入的文字過多時,超過輸入框的行數時,輸入框能夠滾動,這時間問題來了,輸入的提示箭頭會移動到輸入框外,如何解決這個問題呢,查找chromium源碼如下: void LoadIfNecessary(jobject context) { if (load ...
  • 1.代碼如下 (註釋都有) - (void)viewDidLoad { [super viewDidLoad]; UIImageView * bigImageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100) ...
  • 複製並修改原有項目 複製之前創建的項目CC+CV操作 需要改動的地方: * 項目名字 * 應用包名 * R文件重新導包 接著修改件/AndroidManifest.xml中的包名:package="com.wuyudong.rwinrom" 簡單起見直接改為package="com.wuyudong ...
  • ~/Library/MobileDevice/Provisioning Profiles ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...