iOS9之前有两个通讯录相关的框架,AddressBookUI.framework 和 AddressBook.framework。前者包含UI界面,后者无UI界面(如微信电话本),可以自己搭建UI界面。后者是纯C语言的API,仅仅是获得联系人数据,里面的数据大部分是基于Core Foundation框架,使用起来异常麻烦。注意:需要授权才能访问通讯录。iOS9之后出现一个ContactsUI框架,这个框架相比前两者使用起来要方便很多。虽然如此,但是这个从这个框架的名字中可以看出是带有UI界面的,但是很多时候我们可能要自定义,所以除了这个框架,iOS9之前的框架也是很有必要学习的。
先看一下AddressBookUI框架。使用这个框架要导入头文件<ABPeoplePickerNavigationControllerDelegate>,并且要遵守这个协议。
//1.创建联系人选择控制器
ABPeoplePickerNavigationController *picker = [ABPeoplePickerNavigationController new];
//2.设置代理 注意不是delegate
picker.peoplePickerDelegate = self;
//3.模态视图弹出
[self presentViewController:picker animated:YES completion:nil];
下面的是对应的一个代理方法,当选中联系人的时候会调用该方法。
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person{
//需求:获取姓名和电话
//1.获取姓和名
//一个Record就相当于一条记录
//姓
CFStringRef lastName = ABRecordCopyValue(person, kABPersonLastNameProperty);
NSString *lastNameStr = (__bridge NSString *)(lastName);//桥接
NSLog(@"lastName=%@",lastNameStr);
//名
CFStringRef firstName = ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString *firstNameStr = (__bridge NSString *)(firstName);//桥接
NSLog(@"firstName=%@",firstNameStr);
//CF对象一定要释放
CFRelease(lastName);
CFRelease(firstName);
//2.获取电话 注意可能有多个电话
ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
//2.1 获取电话个数 phones.count不可以获取个数
CFIndex count = ABMultiValueGetCount(phones);
//2.2 遍历联系人
for (CFIndex i = 0 ; i < count; i++) {
//2.3 获取联系电话的标签
//两种写法
NSString *label = (__bridge_transfer NSString *)ABMultiValueCopyLabelAtIndex(phones, i);
//NSString *label = CFBridgingRelease(ABMultiValueCopyLabelAtIndex(phones, i));
NSLog(@"电话标签:%@",label);//_$!<Home>!$_
//2.4 获取联系人电话
NSString *value =(__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(phones, i);
NSLog(@"电话号码:%@",value);
}
//3.释放CF对象
CFRelease(phones)
}
//选中某个联系人的某个属性时候调用
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
NSLog(@"%d",property);
}
//取消选中联系人调用
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{
}
关于上面的三个代理方法要稍微说明一下。第一个代理方法,在iOS7下必须能实现此方法,否则会崩溃,在选中摸个联系人的时候会调用此方法。前两个代理方法,如果同时实现只会执行第一个方法。一般情况下只要求执行下面两个方法中的一个即可,如果实现第二个会进入联系人详情界面。
关于iOS7和iOS8的适配问题:
1.在iOS7下必须能实现以下第一个方法,否则会崩溃
2.iOS7可以同时实现两个代理方法,而iOS8只能实现一个(同时实现只会执行第一个方法)
3.iOS7选择联系人代理方法,如果返回YES则会跳到下一个界面,如果返回NO,必须手动实现取消控制器的方法。iOS8默认会在点击联系人的时候,自动取消显示控制器。
注意:如果想适配iOS7和iOS,代理方法必须都写,这样才可以适配7和8
上面涉及这么多和Core Foundation相关的东西,这里就稍微做一些额外的补充。
1. Core Foundation 和 Foundation桥接的问题,桥接有几种方法?
共三种方法,具体如下:
a、CFStringRef lastName = ABRecordCopyValue(person, kABPersonLastNameProperty);
NSString *lastNameStr = (__bridge NSString *)(lastName);
CFRelease(lastName);
b、以下两种写法形式,意思一样。
NSString *label = (__bridge_transfer NSString *)ABMultiValueCopyLabelAtIndex(phones, i);
NSString *label = CFBridgingRelease(ABMultiValueCopyLabelAtIndex(phones, i));
c、
// (__bridge <#type#>)<#expression#>) 只是让Foundation框架暂时使用CF框架对象,一般都要释放CF对象
//(__bridge_transfer <#Objective-C type#>)<#expression#>) CF框架移交管理权给Foundation框架
//(__bridge_retained <#CF type#>)<#expression#>) 前面两个是将CF对象转为Foundation对象,而这个是将Foundation对象转为CF对象,一般不常用
2.注意事项:
CF对象一定要释放,否则出现内存泄露。CFRelease(lastName);