前言:
QQ通讯录、微信电话本会使用iOS的通讯录,iOS中的通讯录是存储在数据库中的,由于iOS的权限设计,开发人员是不允许直接访问通讯录数据库的。在iOS中带有一个Contacts应用程序来管理联系人,但是有些时候我们希望自己的应用能够访问或者修改这些信息,本节我们就来看一下如何访问通讯录中的数据。
iOS关于通讯录的开发有两种,一种是直接调用系统的通讯录界面,根据回调信息处理数据,另一种是直接获取系统的通讯录,完全自定义UI,并且可以通过官方给我们提供的接口进行读写。在iOS9前后,通讯录的访问方式有了重大更新,以前使用的框架都被弃用,本文主要对新的框架的使用做介绍,被弃用的框架会在后面的拓展部分做一下简单说明。
下面是对通讯录访问的几个框架的简单说明:
1. Contacts
iOS9中,苹果介绍了新的Contacts。允许用户使用Objective-C的API和设备的通讯录进行交互,同样适用于Swift语言。比起之前通过AddressBook来读取联系人信息来说,这是一个巨大的进步。因为AddressBook没有Objective-C的API,非常难用,用Swift写的时候更是痛苦。通常情况下,AddressBook不容易理解和掌握,对于新手来说更甚。这所有的一切都成为了历史,新的Contacts框架更加容易理解和使用,完全可以即刻获取通讯录,并可以进行创建和更新操作,与之相关的开发过程也可以被戏剧般地缩短,能够迅速地完成对通讯录的变更和修改。
通讯录的主要数据来源应该存在于设备中的数据库。但是,应用需要通讯录数据时,Contacts框架不会只从数据库中查找。事实上应用软件还会从其他地方查找,像iCloud账号(如果你创建并连接了该账号的话),向应用软件统一返回来自于不同数据源的通讯录数据。这项功能非常有用,因为你不用对设备以外的数据库做单独查询以获得通讯录信息,而是能够按自己的意愿管理,一劳永逸。 使用Contacts 从Framework中返回的联系人是统一的,这意味着,如果你有从不同的数据源来的相同联系人数据,他们会自动合并,无需手动进行合并的操作。
Contacts框架含有许多特定用途的类,所有这些类各司其职。下面我们来看一下Contacts框架中几个常用的类:
- CNContactStore类,是用得最多的一个类,CNContactStore从代码层面代表通讯录数据库,提供不同方法执行相关操作。像获取数据,保存和更新记录,授权查询和授权请求等。
- CNContact类代表通讯录中单独一条记录,但是CNContact一旦在内存中被实例化就是不可变的了。如果你想要创建一条新的,或者对现存的记录做更新的话,那就必须使用CNMutableContact类。
- CNContactFetchRequest类,获取通讯录中通讯信息的请求对象,向应用软件导入通讯录的时候,很少会需要所有的信息。将Contacts框架查询到的所有信息悉数获取的做法会导致进程吞噬资源,除非真得需要使用所有数据,否则应该避免这样的操作。我们可以在初始化CNContactFetchRequest时设置请求的具体参数。例如,你可以获取First Name和Last Name,还可以获取通讯住址,以及电话号码。不动那些不需要的数据,节省了很多资源。
下面是使用Contacts框架的而基本步骤:
- 集成Contacts框架并导入头文件
- 创建通信录对象CNContactStore
- 获取授权状态 并判断授权状态,如果不是已经授权,则直接返回
- 创建获取通信录的请求对象CNContactFetchRequest
- 通过请求遍历通信录 获取所有的联系人
- 获取联系人信息
1.1 获取系统通讯录联系人列表
示例代码:
#import "ViewController.h"
#import <Contacts/Contacts.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
// 获取所有的联系人信息
- (IBAction)getContact:(id)sender{
// 1.创建通信录对象CNContactStore,CNContactStore是一个用来读取和保存联系人的新的类。可以用来进行展示和保存联系人群组操作
CNContactStore *contactStore = [[CNContactStore alloc] init];
// 2.获取授权状态并判断授权状态,如果不是已经授权,则直接返回
[contactStore requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
// 完成授权后就不会再次请求,避免了重复的UI操作
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
if (status != CNAuthorizationStatusAuthorized) return;
// 3.创建获取通信录的请求对象。当我们有了这个联系人数据库的引用后,我们需要创建一个指定条件的请求,通过这个请求去获取某些结果。创建一个CNContactFetchRequest,我们可以通过设置contactkeys的数组,来获取我们需要的结果。
// 3.1 拿到所有打算获取的属性对应的key
NSArray *keys = @[CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey];
// 3.2 创建CNContactFetchRequest对象
CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:keys];
// 4.通过请求遍历通信录 获取所有的联系人,从CNContactStore 中遍历所有符合我们需求的联系人。这个request 没有加任何的条件,所以会返回全部的联系人,包含我们需要的 keys。我们把每一条记录都逐个保存到一个数组中,返回。
[contactStore enumerateContactsWithFetchRequest:request error:nil usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {
// 5.获取联系人信息
// 获取联系人的姓名
NSString *lastname = contact.familyName;
NSString *firstname = contact.givenName;
NSLog(@"%@ %@", lastname, firstname);
// 获取联系人的电话号码
NSArray *phoneNums = contact.phoneNumbers;
for (CNLabeledValue *labeledValue in phoneNums) {
// 2.1.获取电话号码的KEY
NSString *phoneLabel = labeledValue.label;
// 2.2.获取电话号码
CNPhoneNumber *phoneNumer = labeledValue.value;
NSString *phoneValue = phoneNumer.stringValue;
NSLog(@"%@ %@", phoneLabel, phoneValue);
}
}];
}];
}
@end
注意:使用Contacts框架时,特别是在获取通讯录信息的时候,应该在后台线程中执行这些操作。如果获取操作在主线程执行,耗费过多时间的话,那么应用软件会失去响应,最终导致不良的用户体验。
1.2 获取指定的联系人
除了上面我们获取通讯录中所有的联系人之外,我们也可以通过指定谓词的方式来获取想要的联系人信息,Predicate:对于返回的结果来说,NSPredicate对象实际上起到了过滤的作用。这里要强调的重点是,只有CNContact类的谓词(CNContact Predicates)才可以,自己创建的普通谓词不行。在CNContact类所支持的诸多谓词函数里,我们要用到一个叫做predicateForContactsMatchingName的函数。实现通过谓词检索联系人的关键方法就是unifiedContactsMatchingPredicate: keysToFetch:error:方法。下面,我们来指定联系人姓名获取对应的联系人信息。
示例代码:
#import "ViewController.h"
#import <Contacts/Contacts.h>
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
// 通过谓词获取部分通讯录信息
- (IBAction)getContactWithPredicate:(id)sender {
// 0.生成谓词作为请求参数
NSPredicate *predicate = [CNContact predicateForContactsMatchingName:@“张"];
// 1.创建通信录对象CNContactStore
CNContactStore *contactStore = [[CNContactStore alloc] init];
// 2.获取授权状态并判断授权状态
[contactStore requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
CNAuthorizatio