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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...