iOS開發——高級篇——通訊錄


一、簡介


1、如何訪問用戶的通訊錄
1)在iOS9之前
有2個框架可以訪問用戶的通訊錄
AddressBookUI.framework
提供了聯系人列表界面、聯系人詳情界面、添加聯系人界面等
一般用於選擇聯系人

AddressBook.framework
純C語言的API,僅僅是獲得聯系人數據
沒有提供UI界面展示,需要自己搭建聯系人展示界面
里面的數據類型大部分基於Core Foundation框架,使用起來極其蛋疼

從iOS6開始,需要得到用戶的授權才能訪問通訊錄,因此在使用之前,需要檢查用戶是否已經授權
獲得通訊錄的授權狀態:ABAddressBookGetAuthorizationStatus()

2)在iOS9之后
也有2個框架可以訪問用戶的通訊錄
ContactsUI.framework
提供了聯系人列表界面、聯系人詳情界面、添加聯系人界面等
一般用於選擇聯系人

Contacts.framework
沒有提供UI界面展示,需要自己搭建聯系人展示界面


2、授權狀態
kABAuthorizationStatusNotDetermined
用戶還沒有決定是否授權你的程序進行訪問

kABAuthorizationStatusRestricted
iOS設備上一些許可配置阻止程序與通訊錄數據庫進行交互

kABAuthorizationStatusDenied
用戶明確的拒絕了你的程序對通訊錄的訪問

kABAuthorizationStatusAuthorized
用戶已經授權給你的程序對通訊錄進行訪問

 

3、申請訪問通訊錄

// 實例化通訊錄對象
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
    if (granted) {
        NSLog(@"授權成功!");
    } else {
        NSLog(@"授權失敗!");
    }
});
CFRelease(addressBook);

提示:申請通訊錄訪問授權的代碼,通常放在AppDelegate中

 

二、訪問聯系人信息


1、聯系人屬性定義
所有的屬性常量值都定義在了ABPerson.h頭文件中
聯系人屬性包括以下類型:
簡單屬性:姓、名等
多重屬性:電話號碼、電子郵件等
組合屬性:地址等

注意:使用ABRecordCopyValue可以從一條Person記錄中獲取到對應的記錄,但是后續處理則需要根據記錄的具體類型加以區分


2、簡單屬性
一個聯系人就是一個ABRecordRef,每個聯系人都有自己的屬性,比如名字、電話、郵件等
使用ABRecordCopyValue函數可以從ABRecordRef中獲得聯系人的簡單屬性(例如:一個字符串)
ABRecordCopyValue函數接收2個參數
第1個參數是ABRecordRef實例
第2個參數是屬性關鍵字,定義在ABPerson.h中
ABPersonCopyLocalizedPropertyName函數可以根據指定的關鍵字獲取對應的標簽文本


3、獲得所有的聯系人數據

// 獲取所有聯系人記錄
CFArrayRef array = ABAddressBookCopyArrayOfAllPeople(addressBook);
NSInteger count = CFArrayGetCount(array);

for (NSInteger i = 0; i < count; ++i) {
    // 取出一條記錄
    ABRecordRef person = CFArrayGetValueAtIndex(array, i);
    
    // 取出個人記錄中的詳細信息
          //
    CFStringRef firstNameLabel = ABPersonCopyLocalizedPropertyName(kABPersonFirstNameProperty);
    CFStringRef firstName = ABRecordCopyValue(person, kABPersonFirstNameProperty);
    CFStringRef lastNameLabel = ABPersonCopyLocalizedPropertyName(kABPersonLastNameProperty);
    //
    CFStringRef lastName = ABRecordCopyValue(person, kABPersonLastNameProperty);
    
    NSLog(@"%@ %@ - %@ %@", lastNameLabel, lastName, firstNameLabel, firstName);
}


4、CoreFoundation 與 Foundation之間的橋接

// 1. 獲取通訊錄引用
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, nil);
// 2. 獲取所有聯系人記錄
NSArray *array = (__bridge NSArray *)(ABAddressBookCopyArrayOfAllPeople(addressBook));
for (NSInteger i = 0; i < array.count; i++) {
    // 取出一條記錄
        ABRecordRef person = (__bridge ABRecordRef)(array[i]);
    // 取出個人記錄中的詳細信息
        NSString *firstNameLabel = (__bridge NSString *)(ABPersonCopyLocalizedPropertyName(kABPersonFirstNameProperty));
    NSString *firstName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonFirstNameProperty));
    NSString *lastNameLabel = (__bridge NSString *)(ABPersonCopyLocalizedPropertyName(kABPersonLastNameProperty));
    NSString *lastName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonLastNameProperty));
    NSLog(@"%@ %@ - %@ %@", lastNameLabel, lastName, firstNameLabel, firstName);
}
CFRelease(addressBook);

 

5、多重屬性
聯系人的有些屬性值就沒這么簡單,一個屬性可能會包含多個值
比如郵箱,分為工作郵箱、住宅郵箱、其他郵箱等
比如電話,分為工作電話、住宅電話、其他電話等
如果是復雜屬性,那么ABRecordCopyValue函數返回的就是ABMultiValueRef類型的數據,例如郵箱或者電話

// 取電話號碼
ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
// 取記錄數量
NSInteger phoneCount = ABMultiValueGetCount(phones);
// 遍歷所有的電話號碼
for (NSInteger i = 0; i < phoneCount; i++) {
…

 

6、獲取復雜屬性的方法

// 電話標簽
CFStringRef phoneLabel = ABMultiValueCopyLabelAtIndex(phones, i);
// 本地化電話標簽
CFStringRef phoneLocalLabel = ABAddressBookCopyLocalizedLabel(phoneLabel);
// 電話號碼
CFStringRef phoneNumber = ABMultiValueCopyValueAtIndex(phones, i);

 

三、添加修改操作


1、添加聯系人的步驟
通過ABPersonCreate函數創建一個新的聯系人(返回ABRecordRef)
通過ABRecordSetValue函數設置聯系人的屬性
通過ABAddressBookAddRecord函數將聯系人添加到通訊錄數據庫中
通過ABAddressBookSave函數保存剛才所作的修改

可以通過ABAddressBookHasUnsavedChanges函數判斷是否有未保存的修改
當決定是否更改通訊錄數據庫后,你可以分別使用 AbAddressBookSave 或 ABAddressBookRevert 方式來保存或放棄更改


2、添加群組的步驟
添加群組的步驟大體和添加聯系人一致
通過ABPersonCreate函數創建一個新的組(返回ABRecordRef)
通過ABRecordSetValue函數設置組名
通過ABAddressBookAddRecord函數將組添加到通訊錄數據庫中
通過ABAddressBookSave函數保存剛才所作的修改


3、操作聯系人的頭像
想操作聯系人的頭像,有以下函數
BPersonHasImageData
判斷通訊錄中的聯系人是否有圖片

ABPersonCopyImageData
取得圖片數據(假如有的話)

ABPersonSetImageData
設置聯系人的圖片數據

 

四、實戰演練


1、iOS9之前有界面

#import <AddressBookUI/AddressBookUI.h>
// 1.創建一個選擇聯系人的控制器
    ABPeoplePickerNavigationController *ppnc = [[ABPeoplePickerNavigationController alloc] init];
    
    // 2.設置代理
    ppnc.peoplePickerDelegate = self;//<ABPeoplePickerNavigationControllerDelegate>
    
    // 3.彈出控制器
    [self presentViewController:ppnc animated:YES completion:nil];
    
    
    
    #pragma mark - 實現ABPeoplePickerNavigationController的代理方法
/**
 *  當用戶選擇某一個聯系人的時候會執行該方法(如果實現了該方法,那么一定不會執行下面的代理方法)
 *
 *  @param person       選中的聯系人
 */
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person
{
    // 1.獲取用戶的姓名
    CFStringRef lastName = ABRecordCopyValue(person, kABPersonLastNameProperty);
    CFStringRef firstName = ABRecordCopyValue(person, kABPersonFirstNameProperty);
    
    // 將CoreFoundation框架的對象轉成Foundation框架對象,那么可以通過橋接的方式
    // 如果是CoreFoundation框架中的對象,如果是通過copy或者create或者retain,必須對應有一個release
    /*
     __bridge type: 通過該橋接方式,那么CoreFoundation對應的對象需要手動來釋放,Foundation框架的對象如果是在ARC環境下面,則不需手動釋放
     __bridge_transfer type: 通過該橋接方式,那么CoreFoundation對應的對象表示已經交給Foundation對象進行管理,如果是在ARC環境下面,不需要釋放任何一個對象
     */
    NSString *lastname = (__bridge NSString *)(lastName);
    NSString *firstname = (__bridge_transfer NSString *)(firstName);
    NSLog(@"%@ %@", lastname, firstname);
    
    // 2.獲取電話號碼
    
    // 3.釋放對象
    CFRelease(lastName);
}

/**
 *  當用戶選擇某一個聯系人的某一個屬性的時候會執行該方法
 *
 *  @param person       選中的聯系人
 *  @param property     選中的聯系人的屬性
 *  @param identifier   每一個屬性都有一個對應的表示
 */
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
    NSLog(@"%s", __func__);
}

/**
 *  當點擊取消按鈕時,會執行該方法
 *
 */
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker
{
    NSLog(@"%s", __func__);
}

 

2、iOS9之前無界面

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    // 1.獲取授權狀態
    ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
    
    // 2.判斷授權狀態
    if (status == kABAuthorizationStatusNotDetermined) {
        // 3.請求授權
        // 3.1.創建通信錄對象
        ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
        
        // 3.2.請求授權
        ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) { // 當用戶決定是否授權的時候會執行該block
            if (granted) { // 授權成功
                NSLog(@"可以訪問通信錄");
            } else { // 授權失敗
                NSLog(@"不可以訪問通信錄");
            }
        });
        
        // 3.3.釋放不再使用的對象
        CFRelease(addressBook);
    }
    
    return YES;
}


- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 1.判斷授權狀態
    ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
    if (status != kABAuthorizationStatusAuthorized) return;
    
    // 2.獲取聯系人
    // 2.1.創建通信錄對象
    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
    
    // 2.2.獲取所有的聯系人
    CFArrayRef peopleArray = ABAddressBookCopyArrayOfAllPeople(addressBook);
    
    // 2.3.遍歷所有的聯系人
    CFIndex peopleCount = CFArrayGetCount(peopleArray);
    for (int i = 0; i < peopleCount; i++) {
        
        // 3.獲取一條記錄
        ABRecordRef person = CFArrayGetValueAtIndex(peopleArray, i);
        
        // 3.1.獲取聯系人的姓名
        NSString *firstName = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
        NSString *lastName = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
        NSLog(@"%@ %@", firstName, lastName);
        
        // 3.2.獲取電話號碼
        // 3.2.1.獲取所有的電話
        ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
        // 3.3.2.遍歷所有的電話號碼
        CFIndex phoneCount = ABMultiValueGetCount(phones);
        for (int i = 0; i < phoneCount; i++) {
            NSString *phoneLabel = (__bridge_transfer NSString *)ABMultiValueCopyLabelAtIndex(phones, i);
            NSString *phoneValue = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(phones, i);
            NSLog(@"%@ %@", phoneLabel, phoneValue);
        }
    }
}

 


3、三方框架RHAddressBook

#import <AddressBook/AddressBook.h>
#import <RHAddressBook/AddressBook.h>
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    // 1.獲取授權狀態
    RHAuthorizationStatus status = [RHAddressBook authorizationStatus];
    
    // 2.判斷授權狀態
    if (status == RHAuthorizationStatusNotDetermined) {
        // 3.請求授權
        // 3.1.創建通信錄對象
        RHAddressBook *addressBook = [[RHAddressBook alloc] init];
        
        // 3.2.請求授權
        [addressBook requestAuthorizationWithCompletion:^(bool granted, NSError *error) {
            if (granted) {
                NSLog(@"授權成功");
            } else {
                NSLog(@"授權失敗");
            }
        }];
    }
    
    return YES;
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 1.獲取授權狀態
    RHAuthorizationStatus status = [RHAddressBook authorizationStatus];
    
    // 2.如果是已經授權,才能獲取聯系人
    if (status != RHAuthorizationStatusAuthorized) return;
    
    // 3.創建通信錄對象
    RHAddressBook *addressBook = [[RHAddressBook alloc] init];
    
    // 4.獲取所有的聯系人
    NSArray *peopleArray = addressBook.people;
    
    // 5.遍歷所有的記錄
    for (RHPerson *person in peopleArray) {
        // 5.1.獲取用戶的姓名
        NSString *firstname = person.firstName;
        NSString *lastname = person.lastName;
        NSLog(@"%@ %@", firstname, lastname);
        
        // 5.2.獲取電話號碼
        RHMultiValue *phones = person.phoneNumbers;
        
        // 5.3.遍歷所有的電話號碼
        for (int i = 0; i < phones.count; i++) {
            NSString *phoneLabel = [phones labelAtIndex:i];
            NSString *phoneValue = [phones valueAtIndex:i];
            NSLog(@"%@ %@", phoneLabel, phoneValue);
        }
    }
}

 

4、iOS9之后有界面

#import <ContactsUI/ContactsUI.h>
<CNContactPickerDelegate>
    // 1.創建選擇聯系人的界面
    CNContactPickerViewController *cpvc = [[CNContactPickerViewController alloc] init];
    
    // 2.設置代理
    cpvc.delegate = self;
    
    // 3.彈出控制器
    [self presentViewController:cpvc animated:YES completion:nil];
    
#pragma mark - 實現CNContactPickerViewController的代理方法
/**
 *  當用戶選中某一個聯系人的時候會執行該方法
 *
 *  @param contact 選中的聯系人
 */
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact
{
    // 1.獲取聯系人的姓名
    NSString *lastname = contact.familyName;
    NSString *firstname = contact.givenName;
    NSLog(@"%@ %@", lastname, firstname);
    
    // 2.獲取電話號碼
    for (CNLabeledValue *labelValue in contact.phoneNumbers) {
        // 3.獲取電話的label/value
        NSString *phoneLabel = labelValue.label;
        CNPhoneNumber *phoneNumber = labelValue.value;
        NSString *phoneValue = phoneNumber.stringValue;
        NSLog(@"%@ %@", phoneLabel, phoneValue);
    }
}

/**
 *  當用戶選中某一個聯系人的某一個屬性時候會執行該方法
 *
 *  @param contactProperty 選中的屬性
 */
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty
{
    NSLog(@"%s", __func__);
}

 

5、iOS9之后無界面

#import <Contacts/Contacts.h>

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    // 1.獲取授權狀態
    CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
    
    // 2.判斷授權狀態,如果是未決定請求授權
    if (status == CNAuthorizationStatusNotDetermined) {
        // 3.請求授權
        // 3.1.創建CNContactStore對象
        CNContactStore *store = [[CNContactStore alloc] init];
        
        // 3.2.請求授權
        [store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
            if (error) {
                NSLog(@"%@", error);
                return;
            }
            
            if (granted) {
                NSLog(@"授權成功");
            } else {
                NSLog(@"授權失敗");
            }
        }];
    }
    
    return YES;
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 1.判斷授權狀態
    CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
    if (status != CNAuthorizationStatusAuthorized) return;
    
    // 2.創建通信錄對象
    CNContactStore *store = [[CNContactStore alloc] init];
    
    // 3.請求所有的聯系人
    // 3.1.創建聯系人請求對象,並且傳入keys:你准備獲取的信息(姓familyName名givenName 電話號碼:phones)
    NSArray *keys = @[CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey];
    CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:keys];
    
    // 3.2.請求所有的聯系人
    NSError *error = nil;
    [store enumerateContactsWithFetchRequest:request error:&error usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) { // 當遍歷到一條記錄就會執行該block
        // 4.獲取聯系人
        // 4.1.獲取姓名
        NSString *firstName = contact.givenName;
        NSString *lastName = contact.familyName;
        NSLog(@"%@ %@", firstName, lastName);
        
        // 4.2.獲取電話號碼
        NSArray *phones = contact.phoneNumbers;
        for (CNLabeledValue *labelValue in phones) {
            NSString *phoneLabel = labelValue.label;
            CNPhoneNumber *phoneNumber = labelValue.value;
            NSString *phoneValue = phoneNumber.stringValue;
            NSLog(@"%@ %@", phoneLabel, phoneValue);
        }
    }];
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM