iOS中的通訊錄是存儲在數據庫中的,由於iOS的權限設計,開發人員是不允許直接訪問通訊錄數據庫的,實現通訊錄操作需要使用到AddressBook.framework
框架。
iOS還提供了另外一個框架來供開發者操作通訊錄,那就是AddressBookUI.framework
AddressBook.framework
,向外提供現成視圖控制器使用 可以使用ARC管理內存 高度封裝化,界面固定,可定制性差
這兩個框架各有各的優點,各有各的缺點,具體采用哪一種去操作通訊錄看具體需求決定。
AddressBook.framework
框架是基於C語言的,缺少面向對象的思想,所以我們可以把裡面一些結構體理解為一個“類”
ABAddressBookRef
:ABRecordRef
:ABPersonRef
:kABPersonType
的ABRecordRef
代替。 ABGroupRef
:kABGroupType
的ABRecordRef
代替。
/* 獲取一條記錄對象的唯一標識ID */
ABRecordID ABRecordGetRecordID(ABRecordRef record);
/* 創建一條記錄對象,類型為kABPersonType,表示一條聯系人信息 */
ABRecordRef ABPersonCreate(void);
/* 創建一條記錄對象,類型為kABGroupType,表示一條群組信息 */
ABRecordRef ABGroupCreate(void);
/* 獲取指定屬性的值 */
CFTypeRef ABRecordCopyValue(ABRecordRef record, ABPropertyID property);
/* 設置紀錄的屬性值,返回設置是否成功 */
bool ABRecordSetValue(
ABRecordRef record, /* 記錄 */
ABPropertyID property, /* 屬性 */
CFTypeRef value, /* 值,可以是單值,也可以是多重值 */
CFErrorRef* error /* 錯誤信息 */
);
/* 向多重值添加單值 */
bool ABMultiValueAddValueAndLabel(
ABMutableMultiValueRef multiValue, /* 多重值 */
CFTypeRef value, /* 單值 */
CFStringRef label, /* 單值對應的屬性名 */
ABMultiValueIdentifier *outIdentifier /* 多重值的標示 */
);
/* 從多重值中取出指定索引的單值 */
CFTypeRef ABMultiValueCopyValueAtIndex(
ABMultiValueRef multiValue,
CFIndex index
);
/* 刪除指定的屬性值 */
bool ABRecordRemoveValue(
ABRecordRef record, /* 記錄 */
ABPropertyID property, /* 屬性 */
CFErrorRef* error /* 錯誤信息 */
);
/* 創建通訊錄對象 */
ABAddressBookRef ABAddressBookCreate(void);
/* 操作通訊錄用戶授權,注意無論成功與否回調都會調用 */
void ABAddressBookRequestAccessWithCompletion(
ABAddressBookRef addressBook, /* 通訊錄 */
ABAddressBookRequestAccessCompletionHandler completion /* 回調 */
);
/* 獲取通訊錄所有的記錄 */
CFArrayRef ABAddressBookCopyArrayOfAllPeople(ABAddressBookRef addressBook);
/* 添加記錄進通訊錄 */
bool ABAddressBookAddRecord(
ABAddressBookRef addressBook, /* 通訊錄 */
ABRecordRef record, /* 記錄 */
CFErrorRef* error /* 錯誤信息 */
);
/* 從通訊錄刪除記錄 */
bool ABAddressBookRemoveRecord(
ABAddressBookRef addressBook,/* 通訊錄 */
ABRecordRef record, /* 記錄 */
CFErrorRef* error/* 錯誤信息 */
);
/* 從通訊錄中獲取一個記錄,根據記錄ID */
ABRecordRef ABAddressBookGetPersonWithRecordID(
ABAddressBookRef addressBook, /* 通訊錄 */
ABRecordID recordID /* 記錄ID */
);
/* 保持通訊錄,修改了通訊錄需要保存提交修改 */
bool ABAddressBookSave(
ABAddressBookRef addressBook, /* 通訊錄 */
CFErrorRef* error /* 錯誤信息 */
);
ABAddressBookRef
請求用戶授權操作通訊錄ABAddressBookRequestAccessWithCompletion
查詢所有通訊錄的記錄ABAddressBookCopyArrayOfAllPeople
添加記錄,刪除記錄,修改記錄 修改通訊錄後,記住要通訊錄保存ABAddressBookSave
/* 請求訪問通訊錄並獲取通訊錄所有記錄 */
- (void)requestAddressBook{
//創建通訊錄對象
self.addressBook = ABAddressBookCreate();
//請求訪問用戶通訊錄,注意無論成功與否block都會調用
ABAddressBookRequestAccessWithCompletion(self.addressBook, ^(bool granted, CFErrorRef error) {
if (!granted) {
NSLog(@"未獲得通訊錄訪問權限!");
}
//獲取所有通訊錄記錄
[self initAllPerson];
//刷新表格
[self.tableView reloadData];
});
}
/* 取得所有通訊錄記錄 */
- (void)initAllPerson{
//取得通訊錄訪問授權
ABAuthorizationStatus authorization = ABAddressBookGetAuthorizationStatus();
//如果未獲得授權
if (authorization != kABAuthorizationStatusAuthorized) {
NSLog(@"尚未獲得通訊錄訪問授權!");
return ;
}
//取得通訊錄中所有人員記錄
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(self.addressBook);
self.allPerson = (__bridge NSMutableArray *)allPeople;
//釋放資源
CFRelease(allPeople);
}
/**
* 添加一條記錄
*
* @param firstName 名
* @param lastName 姓
* @param iPhoneName iPhone手機號
*/
- (void)addPersonWithFirstName:(NSString *)firstName
lastName:(NSString *)lastName
workNumber:(NSString *)workNumber
{
//創建一條記錄
ABRecordRef recordRef = ABPersonCreate();
//添加名
ABRecordSetValue(recordRef,kABPersonFirstNameProperty,(__bridge CFTypeRef)(firstName),NULL);
//添加姓
ABRecordSetValue(recordRef,kABPersonLastNameProperty,(__bridge CFTypeRef)(lastName),NULL);
//創建一個多值屬性,因為手機號可以有多個
ABMutableMultiValueRef multiValueRef = ABMultiValueCreateMutable(kABStringPropertyType);
//向多值屬性中添加工作電話
ABMultiValueAddValueAndLabel(multiValueRef,(__bridge CFStringRef)(workNumber),kABWorkLabel,NULL);
//添加屬性到指定記錄,這裡添加的是多值屬性
ABRecordSetValue(recordRef, kABPersonPhoneProperty, multiValueRef, NULL);
//添加記錄到通訊錄
ABAddressBookAddRecord(self.addressBook, recordRef, NULL);
//保存通訊錄,提交更改
ABAddressBookSave(self.addressBook, NULL);
//釋放資源
CFRelease(recordRef);
CFRelease(multiValueRef);
}
/* 刪除指定的記錄 */
- (void)removePersonWithRecord:(ABRecordRef)recordRef{
ABAddressBookRemoveRecord(self.addressBook, recordRef, NULL);//刪除
ABAddressBookSave(self.addressBook, NULL);//刪除之後提交更改
}
/* 根據姓名刪除記錄 */
- (void)removePersonWithName:(NSString *)personName{
CFStringRef personNameRef = (__bridge CFStringRef)(personName);
//根據人員姓名查找
CFArrayRef recordsRef = ABAddressBookCopyPeopleWithName(self.addressBook, personNameRef);
CFIndex count = CFArrayGetCount(recordsRef);//取得記錄數
for (CFIndex i=0; i
4. 修改聯系人
/**
* 根據記錄ID修改聯系人信息
*
* @param recordID 記錄唯一ID
* @param firstName 姓
* @param lastName 名
* @param homeNumber 工作電話
*/
- (void)modifyPersonWithRecordID:(ABRecordID)recordID
firstName:(NSString *)firstName
lastName:(NSString *)lastName
workNumber:(NSString *)workNumber
{
//根據記錄ID獲取一條記錄
ABRecordRef recordRef = ABAddressBookGetPersonWithRecordID(self.addressBook, recordID);
//添加名
ABRecordSetValue(recordRef,kABPersonFirstNameProperty,(__bridge CFTypeRef)(firstName),NULL);
//添加姓
ABRecordSetValue(recordRef,kABPersonLastNameProperty,(__bridge CFTypeRef)(lastName),NULL);
//創建一個多值屬性,因為手機號可以有多個
ABMutableMultiValueRef multiValueRef = ABMultiValueCreateMutable(kABStringPropertyType);
//向多值屬性中添加工作電話
ABMultiValueAddValueAndLabel(multiValueRef,(__bridge CFStringRef)(workNumber),kABWorkLabel,NULL);
//添加屬性到指定記錄,這裡添加的是多值屬性
ABRecordSetValue(recordRef, kABPersonPhoneProperty, multiValueRef, NULL);
//保存記錄,提交更改
ABAddressBookSave(self.addressBook, NULL);
//釋放資源
CFRelease(multiValueRef);
}
5. UITableView顯示
#pragma mark - TableView代理和數據源
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
if (!self.allPerson) {
return 0;
}
return self.allPerson.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *key = @"cellIdentify";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:key];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1
reuseIdentifier:key];
}
//取得一條人員記錄
ABRecordRef recordRef = (__bridge ABRecordRef)self.allPerson[indexPath.row];
//取得記錄中得信息,注意這裡進行了強轉,不用自己釋放資源
NSString *firstName = (__bridge NSString *)ABRecordCopyValue(recordRef, kABPersonFirstNameProperty);
NSString *lastName = (__bridge NSString *)ABRecordCopyValue(recordRef, kABPersonLastNameProperty);
//獲取手機號,注意手機號是ABMultiValueRef類,有可能有多條
ABMultiValueRef phoneNumbersRef = ABRecordCopyValue(recordRef, kABPersonPhoneProperty);
long count = ABMultiValueGetCount(phoneNumbersRef);
for(int i = 0;i < count;++i){
NSString *phoneLabel = (__bridge NSString *)(ABMultiValueCopyLabelAtIndex(phoneNumbersRef, i));
NSString *phoneNumber = (__bridge NSString *)(ABMultiValueCopyValueAtIndex(phoneNumbersRef, i));
NSLog(@"%@:%@",phoneLabel,phoneNumber);
}
cell.textLabel.text = [NSString stringWithFormat:@"%@ %@",firstName,lastName];
if (count > 0) {
cell.detailTextLabel.text = (__bridge NSString *)(ABMultiValueCopyValueAtIndex(phoneNumbersRef, 0));
}
//使用cell的tag存儲記錄ID
cell.tag = ABRecordGetRecordID(recordRef);
//記錄最後一個記錄的ID
if (indexPath.row == self.allPerson.count - 1) {
self.lastID = ABRecordGetRecordID(recordRef);
}
return cell;
}
6. UI點擊以及視圖控制器初始化和銷毀
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
//請求訪問通訊錄並獲取通訊錄所有記錄
[self requestAddressBook];
}
//由於在整個視圖控制器周期內addressBook都駐留在內存中,所以當控制器視圖銷毀時銷毀該對象
- (void)dealloc{
if (self.addressBook != NULL) {
CFRelease(self.addressBook);
}
}
#pragma mark - UI點擊
- (IBAction)addPerson:(id)sender {
//添加聯系人
[self addPersonWithFirstName:@"liu"
lastName:@"ting"
workNumber:@"13412321332"];
//獲取所有通訊錄記錄
[self initAllPerson];
//刷新表格
[self.tableView reloadData];
}
- (IBAction)removePerson:(id)sender {
//刪除聯系人
[self removePersonWithName:@"liu ting"];
//獲取所有通訊錄記錄
[self initAllPerson];
//刷新表格
[self.tableView reloadData];
}
- (IBAction)changePerson:(id)sender {
[self modifyPersonWithRecordID:self.lastID
firstName:@"XXXX"
lastName:@"YYY"
workNumber:@"1111111111"];
//獲取所有通訊錄記錄
[self initAllPerson];
//刷新表格
[self.tableView reloadData];
}
三、AddressBookUI
AddressBookUI
這個框架就提供了現成的控制器視圖供開發者使用,高度封裝化。
下面是這個框架中提供的控制器視圖:
ABPersonViewController
:
用於查看聯系人信息(可設置編輯)。
需要設置displayedPerson
屬性來設置要顯示或編輯的聯系人。 ABNewPersonViewController
:
用於新增聯系人信息。 ABUnknownPersonViewController
:
用於顯示一個未知聯系人(尚未保存的聯系人)信息。
需要設置displayedPerson
屬性來設置要顯示的未知聯系人。 ABPeoplePickerNavigationController
:
用於選擇聯系人。
前面三個控制器視圖均繼承於
UIViewController
,在使用過程中必須使用一個UINavigationController
進行包裝,否則只能看到視圖內容無法進行操作,並且必須處理控制器的關閉操作,可以通過代理方法獲得新增、修改的聯系人。
最後一個控制器視圖繼承於UINavigationController
,視圖自身的“組”、“取消”按鈕操作不需要開發者來完成,例如開發者不用在點擊取消時關閉當前控制器視圖,它自身已經實現了關閉方法。
下面是這四個控制器的代理方法:
#pragma mark - ABPersonViewController代理方法
//選擇一個人員屬性後觸發,返回值YES表示觸發默認行為操作,否則執行代理中自定義的操作
- (BOOL)personViewController:(ABPersonViewController *)personViewController
shouldPerformDefaultActionForPerson:(ABRecordRef)person
property:(ABPropertyID)property
identifier:(ABMultiValueIdentifier)identifier;
#pragma mark - ABNewPersonViewController代理方法
/*
完成新增(點擊取消和完成按鈕時調用),注意這裡不用做實際的通訊錄增加工作,
此代理方法調用時已經完成新增,當保存成功的時候參數中得person會返回保存的記錄,如果點擊取消person為NULL
*/
- (void)newPersonViewController:(ABNewPersonViewController *)newPersonView
didCompleteWithNewPerson:(ABRecordRef)person;
#pragma mark - ABUnknownPersonViewController代理方法
//保存未知聯系人時觸發
- (void)unknownPersonViewController:(ABUnknownPersonViewController *)unknownCardViewController
didResolveToPerson:(ABRecordRef)person;
#pragma mark - ABPeoplePickerNavigationController代理方法
//選擇一個聯系人後調用,注意這個代理方法實現後選擇屬性的方法將不會再調用
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
didSelectPerson:(ABRecordRef)person;
//選擇屬性之後調用,注意如果上面的代理方法實現後此方法不會被調用
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
didSelectPerson:(ABRecordRef)person
property:(ABPropertyID)property
identifier:(ABMultiValueIdentifier)identifier;
//點擊取消按鈕調用
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker;
下面是具體代碼示例【我包裝了一個全局導航控制器】:
#import "addressBookUIViewController.h"
#import
@interface addressBookUIViewController ()
@end
@implementation addressBookUIViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
#pragma mark - UI事件
//點擊添加聯系人
- (IBAction)addPersonClick:(UIButton *)sender {
//創建添加聯系人視圖控制器
ABNewPersonViewController *newPersonController =
[[ABNewPersonViewController alloc] init];
//設置代理
newPersonController.newPersonViewDelegate = self;
//注意必須有一層導航控制器才能使用,否則不會出現取消和完成按鈕,無法進行保存等操作
[self.navigationController pushViewController:newPersonController animated:YES];
}
//點擊未知聯系人
- (IBAction)unknownPersonClick:(UIButton *)sender {
//創建未知聯系人視圖控制器
ABUnknownPersonViewController *unknownPersonController =
[[ABUnknownPersonViewController alloc] init];
//設置未知人員
ABRecordRef recordRef=ABPersonCreate();
ABRecordSetValue(recordRef, kABPersonFirstNameProperty, @"Kenshin", NULL);
ABRecordSetValue(recordRef, kABPersonLastNameProperty, @"Cui", NULL);
ABMultiValueRef multiValueRef = ABMultiValueCreateMutable(kABStringPropertyType);
ABMultiValueAddValueAndLabel(multiValueRef, @"18500138888", kABHomeLabel, NULL);
ABRecordSetValue(recordRef, kABPersonPhoneProperty, multiValueRef, NULL);
unknownPersonController.displayedPerson = recordRef;
//設置代理
unknownPersonController.unknownPersonViewDelegate = self;
//設置其他屬性
unknownPersonController.allowsActions = YES;//顯示標准操作按鈕
unknownPersonController.allowsAddingToAddressBook = YES;//是否允許將聯系人添加到地址簿
//釋放資源
CFRelease(multiValueRef);
CFRelease(recordRef);
[self.navigationController pushViewController:unknownPersonController animated:YES];
}
//點擊顯示聯系人
- (IBAction)showPersonClick:(UIButton *)sender {
//創建顯示聯系人視圖控制器
ABPersonViewController *personController = [[ABPersonViewController alloc] init];
//設置聯系人,取得id為1的聯系人記錄
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
ABRecordRef recordRef = ABAddressBookGetPersonWithRecordID(addressBook, 1);
personController.displayedPerson = recordRef;
//設置代理
personController.personViewDelegate = self;
//設置其他屬性
personController.allowsActions = YES;//是否顯示發送信息、共享聯系人等按鈕
personController.allowsEditing = YES;//允許編輯
[self.navigationController pushViewController:personController animated:YES];
}
//點擊選擇聯系人
- (IBAction)selectPersonClick:(UIButton *)sender {
//創建選擇聯系人導航視圖控制器
ABPeoplePickerNavigationController *peoplePickerController =
[[ABPeoplePickerNavigationController alloc] init];
//設置代理
peoplePickerController.peoplePickerDelegate = self;
//以模態彈出
[self presentViewController:peoplePickerController animated:YES completion:nil];
}
#pragma mark - ABNewPersonViewController代理方法
/*
完成新增(點擊取消和完成按鈕時調用),注意這裡不用做實際的通訊錄增加工作,
此代理方法調用時已經完成新增,當保存成功的時候參數中得person會返回保存的記錄,
如果點擊取消person為NULL
*/
- (void)newPersonViewController:(ABNewPersonViewController *)newPersonView
didCompleteWithNewPerson:(ABRecordRef)person
{
//如果有聯系人信息
if (person) {
NSLog(@"%@ 信息保存成功.",(__bridge NSString *)(ABRecordCopyCompositeName(person)));
}else{
NSLog(@"點擊了取消.");
}
//返回主視圖窗口
[self.navigationController popToRootViewControllerAnimated:YES];
}
#pragma mark - ABUnknownPersonViewController代理方法
//保存未知聯系人時觸發
- (void)unknownPersonViewController:(ABUnknownPersonViewController *)unknownCardViewController
didResolveToPerson:(ABRecordRef)person
{
if (person) {
NSLog(@"%@ 信息保存成功!",(__bridge NSString *)(ABRecordCopyCompositeName(person)));
}
//返回主視圖窗口
[self.navigationController popToRootViewControllerAnimated:YES];
}
#pragma mark - ABPersonViewController代理方法
//選擇一個人員屬性後觸發,返回值YES表示觸發默認行為操作,否則執行代理中自定義的操作
- (BOOL)personViewController:(ABPersonViewController *)personViewController
shouldPerformDefaultActionForPerson:(ABRecordRef)person
property:(ABPropertyID)property
identifier:(ABMultiValueIdentifier)identifier
{
if (person) {
NSLog(@"選擇了屬性:%d",property);
NSLog(@"值為:%@", (__bridge NSString *)ABRecordCopyValue(person, property));
}
return NO;
}
#pragma mark - ABPeoplePickerNavigationController代理方法
//選擇一個聯系人後調用,注意這個代理方法實現後選擇屬性的方法將不會再調用
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
didSelectPerson:(ABRecordRef)person
{
if (person) {
NSLog(@"選擇了%@.",(__bridge NSString *)(ABRecordCopyCompositeName(person)));
}
}
//點擊取消按鈕後調用
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{
NSLog(@"取消選擇.");
}
@end