iOS訪問通訊錄開發-讀取聯係人信息
讀取通信錄中的聯係人一般的過程是先查找聯係人記錄,然後再訪問記錄的屬性,屬性又可以分為單值屬性和多值屬性。通過下麵例子介紹聯係人的查詢,以及單值屬性和多值屬性的訪問,還有讀取聯係人中的圖片數據。
本案例是從iOS設備上讀取通訊錄中的聯係人,並將其顯示在一個表視圖中,可以進行查詢,點擊聯係人進入詳細信息畫麵。訪問通訊錄的應用必須要做的兩件事情:
1、添加AddressBook和AddressBookUI框架
為工程添加AddressBook.framework和AddressBookUI.framework
2、引入頭文件
在需要訪問通訊錄類的頭文件中引入下麵頭文件:
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>
查詢聯係人記錄
在從通信錄數據庫查詢聯係人數據是無法使用SQL語句,隻能通過ABAddressBookCopyArrayOfAllPeople和ABAddressBookCopyPeopleWithName函數獲得,它們的定義如下:
CFArrayRef ABAddressBookCopyArrayOfAllPeople ( ABAddressBookRef addressBook ); CFArrayRef ABAddressBookCopyPeopleWithName ( ABAddressBookRef addressBook, CFStringRef name );
ABAddressBookCopyArrayOfAllPeople函數是查詢所有的聯係人數據。ABAddressBookCopyPeopleWithName函數是通過人名查詢通訊錄中的聯係人,其中的name參數就是查詢的前綴關鍵字。兩個函數中都有addressBook參數,它是我們要查詢的通訊錄對象,其創建使用ABAddressBookCreateWithOptions函數(在iOS6之前是ABAddressBookCreate函數),它的定義:
ABAddressBookRef ABAddressBookCreateWithOptions ( CFDictionaryRef options, CFErrorRef* error );
options參數是保留參數,目前沒有采用,使用時候可以傳遞NULL值。error是錯誤對象,包含錯誤信息。
下麵是我們代碼中有關係查詢的部分,先看一下ViewController.h:
#import <UIKit/UIKit.h> #import <AddressBook/AddressBook.h> #import ”DetailViewController.h” @interface ViewController : UITableViewController <UISearchBarDelegate, UISearchDisplayDelegate> @property (nonatomic, strong) NSArray *listContacts; - (void)filterContentForSearchText:(NSString*)searchText; @end
屬性listContacts是裝載聯係人記錄數組集合,filterContentForSearchText:方法是用來過濾聯係人信息的方法,也就是查詢方法。
ViewController.m中的viewDidLoad方法:
- (void)viewDidLoad { [super viewDidLoad]; CFErrorRef error = NULL; ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error); ① ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) { ② if (granted) { //查詢所有 [self filterContentForSearchText:@""]; ③ } }); CFRelease(addressBook); ④ }
在viewDidLoad方法中首先在第①行代碼處使用ABAddressBookCreateWithOptions函數創建addressBook對象,然後在第②行又調用了函數ABAddressBookRequestAccessWithCompletion,這個函數用於向用戶請求訪問通訊錄數據庫,如果是第一次訪問,則會彈出一個用戶授權對話框,如果用戶授權可以訪問則會調用下麵的代碼塊。
^(bool granted, CFErrorRef error) { if (granted) { } });
由於請求和代碼塊的回調都是異步的,你會發現表視圖畫麵先出現,然後過一會兒才有查詢出來的結果。在iOS6之後這個請求過程必須有的,否則無法訪問通訊錄數據庫。
ViewController.m中的filterContentForSearchText:查詢方法:
- (void)filterContentForSearchText:(NSString*)searchText { //如果沒有授權則退出 if (ABAddressBookGetAuthorizationStatus() != kABAuthorizationStatusAuthorized) { return ; } CFErrorRef error = NULL; ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error); if([searchText length]==0) { //查詢所有 self.listContacts = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(addressBook)); } else { //條件查詢 CFStringRef cfSearchText = (CFStringRef)CFBridgingRetain(searchText); self.listContacts = CFBridgingRelease(ABAddressBookCopyPeopleWithName(addressBook, cfSearchText)); CFRelease(cfSearchText); } [self.tableView reloadData]; CFRelease(addressBook); }
在該方法中實現查詢,ABAddressBookGetAuthorizationStatus()函數返回應用的授權狀態,其中kABAuthorizationStatusAuthorized常量代表用戶已經授權,在沒有授權情況下該方法不進行任何處理。ABAddressBookCopyArrayOfAllPeople函數是查詢所有數據,ABAddressBookCopyPeopleWithName函數是根據條件查詢,返回值是CFArrayRef類型,不能直接賦值給listContacts(NSArray*類型)屬性,處理方式一般如下兩種:
self.listContacts = (__bridge NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook) ;
或
self.listContacts = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(addressBook));
(__bridge NSArray *)方式不會轉讓對象所有權,隻是簡單強製轉化。CFBridgingRelease函數實現的是Core Foundation類型到Foundation類型轉化並把對象所有權轉讓ARC(自動管理引用計數),因此不需要釋放屬性listContacts對應的成員變量。類似還有CFBridgingRetain函數,實現的是Foundation類型到Core Foundation類型轉化, 並把對象所有權轉讓調用者,因此需要釋放這個對象,代碼如下:
CFStringRef cfSearchText = (CFStringRef)CFBridgingRetain(searchText); self.listContacts = CFBridgingRelease(ABAddressBookCopyPeopleWithName(addressBook, cfSearchText)); CFRelease(cfSearchText);
最後在第④行調用CFRelease(addressBook)函數釋放addressBook對象,Core Foundation框架中的數據類型內存管理是不受ARC管理的,但是與Foundation框架的MRC管理類似,需要手動釋放,CFRelease函數就是相當於Foundation框架中的release(或autorelease)方法。
ViewController.m中的SearchBar查詢相關方法:
#pragma mark –UISearchBarDelegate 協議方法
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar { //查詢所有 [self filterContentForSearchText:@""]; } #pragma mark - UISearchDisplayController Delegate Methods //當文本內容發生改變時候,向表視圖數據源發出重新加載消息 - (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString { [self filterContentForSearchText:searchString]; //YES情況下表視圖可以重新加載 return YES; }
讀取單值屬性
在一條聯係人記錄中,有很多屬性,這些屬性有單值屬性和多值屬性,單值屬性是隻有一個值的屬性,如:姓氏、名字等,它們是由下麵常量定義的:
kABPersonFirstNameProperty,名字
kABPersonLastNameProperty,姓氏
kABPersonMiddleNameProperty,中間名
kABPersonPrefixProperty,前綴
kABPersonSuffixProperty,後綴
kABPersonNicknameProperty,昵稱
kABPersonFirstNamePhoneticProperty,名字漢語拚音或音標
kABPersonLastNamePhoneticProperty,姓氏漢語拚音或音標
q kABPersonMiddleNamePhoneticProperty,中間名漢語拚音或音標
kABPersonOrganizationProperty,組織名
kABPersonJobTitleProperty,頭銜
kABPersonDepartmentProperty,部門
kABPersonNoteProperty,備注
讀取記錄屬性函數是ABRecordCopyValue,ABRecordCopyValue函數的定義如下:
CFTypeRef ABRecordCopyValue ( ABRecordRef record, ABPropertyID property );
ABRecordRef參數是記錄對象,ABPropertyID是屬性ID,就是上麵的常量kABPersonFirstNameProperty等。返回值類型是CFTypeRef,它是Core Foundation類型的“泛型”,可以代表任何的Core Foundation類型。
ViewController.m中的tableView:cellForRowAtIndexPath:方法,主要實現了訪問單值屬性:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @”Cell”; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } ABRecordRef thisPerson = CFBridgingRetain([self.listContacts objectAtIndex:[indexPath row]]); ① NSString *firstName = CFBridgingRelease(ABRecordCopyValue(thisPerson, kABPersonFirstNameProperty)); ② firstName = firstName != nil?firstName:@”"; NSString *lastName = CFBridgingRelease(ABRecordCopyValue(thisPerson, kABPersonLastNameProperty)); ③ lastName = lastName != nil?lastName:@”"; cell.textLabel.text = [NSString stringWithFormat:@"%@ %@",firstName,lastName]; CFRelease(thisPerson); return cell; }
第①行ABRecordRef thisPerson = CFBridgingRetain([self.listContacts objectAtIndex:[indexPath row]])語句是從NSArray*集合中取出一個元素,並且轉化為Core Foundation類型的ABRecordRef類型。CFBridgingRelease(ABRecordCopyValue(thisPerson, kABPersonFirstNameProperty))語句是將名字屬性取出來,轉化為NSString*類型。最後CFRelease(thisPerson)是釋放ABRecordRef對象。
此外,為了把選中的聯係人傳遞給詳細畫麵,我們需要獲得選中記錄的ID,然後把ID傳遞到詳細畫麵,這個過程處理是在ViewController.m中的 prepareForSegue:方法完成的:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([[segue identifier] isEqualToString:@”showDetail”]) { NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; ABRecordRef thisPerson = CFBridgingRetain([self.listContacts objectAtIndex:[indexPath row]]); DetailViewController *detailViewController = [segue destinationViewController]; ABRecordID personID = ABRecordGetRecordID(thisPerson); ① NSNumber *personIDAsNumber = [NSNumber numberWithInt:personID]; ② detailViewController.personIDAsNumber = personIDAsNumber; ③ CFRelease(thisPerson); ④ } }
其中第①行代碼調用函數ABRecordGetRecordID是獲取選中記錄的ID,其中ID為ABRecordID類型。為了傳遞這個ID給DetailViewController視圖控製器,DetailViewController視圖控製器定義了personIDAsNumber屬性,在第③行將ID給personIDAsNumber屬性。DetailViewController.h代碼如下:
#import <UIKit/UIKit.h>
#import <AddressBook/AddressBook.h>
@interface DetailViewController : UITableViewController
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UILabel *lblName;
@property (weak, nonatomic) IBOutlet UILabel *lblMobile;
@property (weak, nonatomic) IBOutlet UILabel *lblIPhone;
@property (weak, nonatomic) IBOutlet UILabel *lblWorkEmail;
@property (weak, nonatomic) IBOutlet UILabel *lblHomeEmail;
@property (strong, nonatomic) NSNumber* personIDAsNumber;
@end
personIDAsNumber屬性為NSNumber*類型。
讀取多值屬性
多值屬性是包含多個值的集合類型,如:電話號碼、Email、URL等,它們主要是由下麵常量定義的:
kABPersonPhoneProperty,電話號碼屬性,kABMultiStringPropertyType類型多值屬性;
kABPersonEmailProperty,Email屬性,kABMultiStringPropertyType類型多值屬性;
kABPersonURLProperty,URL屬性,kABMultiStringPropertyType類型多值屬性;
kABPersonRelatedNamesProperty,親屬關係人屬性,kABMultiStringPropertyType類型多值屬性;
kABPersonAddressProperty,地址屬性,kABMultiDictionaryPropertyType類型多值屬性;
kABPersonInstantMessageProperty,即時聊天屬性,kABMultiDictionaryPropertyType類型多值屬性;
kABPersonSocialProfileProperty,社交賬號屬性,kABMultiDictionaryPropertyType類型多值屬性;
在多值屬性中包含了label(標簽)、value(值)和ID等部分,其中標簽和值都是可以重複的,而ID是不能重複的
多值屬性訪問方式與單值屬性訪問類似都使用ABRecordCopyValue函數。不同的是多值屬性訪問返回值是ABMultiValueRef,然後要使用ABMultiValueCopyArrayOfAllValues函數從ABMultiValueRef對象中獲取數組CFArrayRef集合。ABMultiValueCopyArrayOfAllValues函數的定義如下:
CFArrayRef ABMultiValueCopyArrayOfAllValues ( ABMultiValueRef multiValue ); ABMultiValueCopyLabelAtIndex函數可以從ABMultiValueRef對象中返回標簽,其定義如下: CFStringRef ABMultiValueCopyLabelAtIndex ( ABMultiValueRef multiValue, CFIndex index );
參數multiValue是ABMultiValueRef對象,index是查找標簽的索引。
ABMultiValueGetIdentifierAtIndex函數可以從ABMultiValueRef對象中返回ID,其定義如下:
ABMultiValueIdentifier ABMultiValueGetIdentifierAtIndex ( ABMultiValueRef multiValue, CFIndex index ); 在DetailViewController.m文件viewDidLoad方法中取得Email多值屬性,其代碼如下: ABMultiValueRef emailsProperty = ABRecordCopyValue(person, kABPersonEmailProperty); NSArray* emailsArray = CFBridgingRelease(ABMultiValueCopyArrayOfAllValues(emailsProperty)); for(int index = 0; index< [emailsArray count]; index++){ NSString *email = [emailsArray objectAtIndex:index]; NSString *emailLabel = CFBridgingRelease(ABMultiValueCopyLabelAtIndex(emailsProperty, index)); if ([emailLabel isEqualToString:(NSString*)kABWorkLabel]) { [self.lblWorkEmail setText:email]; } else if ([emailLabel isEqualToString:(NSString*)kABHomeLabel]) { [self.lblHomeEmail setText:email]; } else { NSLog(@”%@: %@”, @”其它Email”, email); } } CFRelease(emailsProperty);
其中ABMultiValueCopyArrayOfAllValues(emailsProperty))語句是從emailsProperty屬性中取出數組集合。kABWorkLabel和kABHomeLabel都是Email多值屬性的標簽。kABWorkLabel是工作Email標簽和kABHomeLabel是家庭Email標簽,另外還有kABOtherLabel,它是Email標簽。最後emailsProperty需要釋放。
DetailViewController.m中的viewDidLoad方法中取得電話號碼多值屬性代碼如下:
ABMultiValueRef phoneNumberProperty = ABRecordCopyValue(person, kABPersonPhoneProperty); NSArray* phoneNumberArray = CFBridgingRelease(ABMultiValueCopyArrayOfAllValues(phoneNumberProperty)); for(int index = 0; index< [phoneNumberArray count]; index++){ NSString *phoneNumber = [phoneNumberArray objectAtIndex:index]; NSString *phoneNumberLabel = CFBridgingRelease(ABMultiValueCopyLabelAtIndex(phoneNumberProperty, index)); if ([phoneNumberLabel isEqualToString:(NSString*)kABPersonPhoneMobileLabel]) { [self.lblMobile setText:phoneNumber]; } else if ([phoneNumberLabel isEqualToString:(NSString*)kABPersonPhoneIPhoneLabel]) { [self.lblIPhone setText:phoneNumber]; } else { NSLog(@”%@: %@”, @”其它電話”, phoneNumber); } } CFRelease(phoneNumberProperty);
kABPersonPhoneMobileLabel和kABPersonPhoneIPhoneLabel都是電話號碼屬性的標簽。kABPersonPhoneMobileLabel是移動電話號碼標簽,kABPersonPhoneIPhoneLabel是iPhone電話號碼標簽。此外還有:
kABPersonPhoneMainLabel,主要電話號碼標簽;
kABPersonPhoneHomeFAXLabel,家庭傳真電話號碼標簽;
kABPersonPhoneWorkFAXLabel,工作傳真電話號碼標簽;
kABPersonPhonePagerLabel,尋唿機號碼標簽。
讀取圖片屬性
通訊錄中的聯係人可以有一個圖片,讀取聯係人圖片的相關函數有ABPersonCopyImageData和ABPersonHasImageData等。ABPersonCopyImageData可以讀取聯係人圖片函數,它的定義如下:
CFDataRef ABPersonCopyImageData ( ABRecordRef person );
它的返回類型是CFDataRef,與之對應的Foundation框架類型是NSData*。ABPersonHasImageData函數用於判斷聯係人是否有圖片,它的定義如下:
bool ABPersonHasImageData ( ABRecordRef person ); DetailViewController.m中的viewDidLoad方法中取得聯係人圖片代碼如下: if (ABPersonHasImageData(person)) { NSData *photoData = CFBridgingRelease(ABPersonCopyImageData(person)); if(photoData){ [self.imageView setImage:[UIImage imageWithData:photoData]]; } }
ABPersonCopyImageData取出的是CFDataRef類型,將其轉化為NSData*,再使用UIImage的構造方法imageWithData:構建UIImage對象,然後再把UIImage對象賦值給imageView圖片控件。
最後更新:2017-04-03 16:59:46