一、通讯录

iOS中的通讯录是存储在数据库中的,由于iOS的权限设计,开发人员是不允许直接访问通讯录数据库的,实现通讯录操作需要使用到AddressBook.framework框架。

AddressBook.framework框架:
  • 可以从底层去操作通讯录的所有信息,做到精确控制
  • 是基于C语言编写的,无法使用ARC管理内存,需要开发者手动管理内存
  • 需要自构UI界面

iOS还提供了另外一个框架来供开发者操作通讯录,那就是AddressBookUI.framework

AddressBookUI.framework框架:
  • 该框架封装AddressBook.framework,向外提供现成视图控制器使用
  • 可以使用ARC管理内存
  • 高度封装化,界面固定,可定制性差

这两个框架各有各的优点,各有各的缺点,具体采用哪一种去操作通讯录看具体需求决定。

二、AddressBook

AddressBook.framework框架是基于C语言的,缺少面向对象的思想,所以我们可以把里面一些结构体理解为一个“类”

首先我们来了解几个核心结构体:
  1. ABAddressBookRef
    通讯录对象,全局管理通讯录操作,比如修改保存等
  2. ABRecordRef
    通用的记录对象,可以是一条联系人信息,也可以是一个群组,通过具体类型进行区分,每条记录都有一个唯一ID标识
  3. ABPersonRef
    联系人信息,不常用,可以用类型为kABPersonTypeABRecordRef代替。
  4. ABGroupRef
    群组信息,不常用,可以用类型为kABGroupTypeABRecordRef代替。
常使用到关于记录Record的C语言函数:
/* 获取一条记录对象的唯一标识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 /* 错误信息 */
);
通讯录使用步骤:
  1. 创建通讯录对象ABAddressBookRef
  2. 请求用户授权操作通讯录ABAddressBookRequestAccessWithCompletion
  3. 查询所有通讯录的记录ABAddressBookCopyArrayOfAllPeople
  4. 添加记录,删除记录,修改记录
  5. 修改通讯录后,记住要通讯录保存ABAddressBookSave
下面是实际代码:
1. 创建通讯录并请求授权
/* 请求访问通讯录并获取通讯录所有记录 */
- (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);
}
2. 添加联系人

/**
* 添加一条记录
*
* @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);
}
3. 删除联系人
/* 删除指定的记录 */
- (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<count; ++i) {
ABRecordRef recordRef = CFArrayGetValueAtIndex(recordsRef, i);//取得指定的记录
ABAddressBookRemoveRecord(self.addressBook, recordRef, NULL);//删除
}
//删除之后提交更改
ABAddressBookSave(self.addressBook, NULL);
CFRelease(recordsRef);
}
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这个框架就提供了现成的控制器视图供开发者使用,高度封装化。

下面是这个框架中提供的控制器视图:
  1. ABPersonViewController
    用于查看联系人信息(可设置编辑)。
    需要设置displayedPerson属性来设置要显示或编辑的联系人。
  2. ABNewPersonViewController
    用于新增联系人信息。
  3. ABUnknownPersonViewController
    用于显示一个未知联系人(尚未保存的联系人)信息。
    需要设置displayedPerson属性来设置要显示的未知联系人。
  4. 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 <AddressBookUI/AddressBookUI.h> @interface addressBookUIViewController () <ABNewPersonViewControllerDelegate,
ABUnknownPersonViewControllerDelegate,
ABPeoplePickerNavigationControllerDelegate,
ABPersonViewControllerDelegate> @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

最新文章

  1. 算法系列:XXX
  2. Python模拟C++输出流
  3. git tag查看、创建与删除
  4. Camera拍照声设定
  5. 二维码zxing源码分析(二)decode部分
  6. Visual Studio下,打开资源视图rc文件,提示在另一个编辑器中打开
  7. 解决jQuery对表单serialize后出现的乱码问题
  8. jquery.scrollTo-min.js
  9. HDU_2027——统计元音
  10. OOP 三大特点:继承性,封装性,多态性
  11. 打印中文dict list的各种姿势
  12. 八年架构师大咖首次揭秘,年薪50W秘籍!
  13. Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AOP编程比较
  14. linux命令去掉_bak
  15. 数位dp 模板
  16. Yii2增删改查
  17. UNIGUI与UNIURLFRAME的互动
  18. 利用pandas将numpy数组导出生成excel
  19. python第一类对象,闭包,迭代器
  20. 利用存储过程来重命名SQL Server数据库

热门文章

  1. LeetCode House Robber 家庭劫犯(dp)
  2. 2018.5.8 XML编程
  3. CentOS为用户增加root权限
  4. python之生成器的初识
  5. Your Ride Is Here
  6. C++ 学习笔记(一) cout 与printf 的不同之处
  7. 浅谈一类「AC自动机计数」问题
  8. numpy中常用的函数
  9. linux时区
  10. python中的内建函数