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图片控件。
iOS访问通讯录开发-读取联系人信息的更多相关文章
- 通过 ContentResolver 读取联系人信息
1.首先动态获取 读取联系人信息权限 <1>配置文件中声明对应权限 ) } ] == PackageManager.PERMISSION_GRANTED) { readContact ...
- android之读取联系人信息
联系人信息被存放在一个contacts2.db的数据库中 主要的两张表 读取联系人 读取联系人需要知道联系人内容提供者的地址,以及对应的操作对象.一般情况下操作对象是的命名方式和表明是一致的. 布局文 ...
- IOS 获取系统通讯录中的联系人信息
- (IBAction)getAllContactFromSystem { ABAddressBookRef ab = ABAddressBookCreateWithOptions(NULL, NUL ...
- IOS开发- 访问通讯录,并将通讯录中姓名-头像-手机号码 发给服务器
现在很多软件都会访问通讯录,并将通讯录的信息取得,发给服务器,然后服务器会返回相应电话号码的用户是否注册. 现在分享一下前两步,访问通讯录并处理通讯录的信息 1.导入框架 #import <Ad ...
- iOS 修改通讯录联系人地址(address)崩溃原因分析
目前项目中需要对iOS系统通讯录进行读取,修改操作.在进行对地址修改的时候,出现了一个奇怪现象: ● 如果contact没有address字段(或者一个全新的contact),对它的address进行 ...
- Android项目——读取手机联系人信息
加入读取联系人信息的权限 <uses-permission android:name="android.permission.READ_CONTACTS"/> cont ...
- IOS开发笔记 IOS如何访问通讯录
IOS开发笔记 IOS如何访问通讯录 其实我是反对这类的需求,你说你读我的隐私,我肯定不愿意的. 幸好ios6.0 以后给了个权限控制.当打开app的时候你可以选择拒绝. 实现方法: [plain] ...
- Android软件开发之获取通讯录联系人信息
Android手机的通讯录联系人全部都存在系统的数据库中,如果须要获得通讯里联系人的信息就须要访问系统的数据库,才能将信息拿出来. 这一篇文章我主要带领同学们熟悉Android的通讯录机制. 图中选中 ...
- iOS获取通讯录所有联系人信息
以下是2种方式: 第一种方法: GetAddressBook.h #import <Foundation/Foundation.h> @interface GetAddressBook : ...
随机推荐
- BZOJ 1497: [NOI2006]最大获利 最小割
1497: [NOI2006]最大获利 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=1497 Description 新的技术正冲击着手 ...
- codeforces Gym 100187L L. Ministry of Truth 水题
L. Ministry of Truth Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100187/p ...
- 《Java并发编程实战》第三章 对象的共享 读书笔记
一.可见性 什么是可见性? Java线程安全须要防止某个线程正在使用对象状态而还有一个线程在同一时候改动该状态,并且须要确保当一个线程改动了对象的状态后,其它线程能够看到发生的状态变化. 后者就是可见 ...
- C#MongoDB 分页查询的方法及性能
传统的SQL分页 传统的sql分页,所有的方案几乎是绕不开row_number的,对于需要各种排序,复杂查询的场景,row_number就是杀手锏.另外,针对现在的web很流行的poll/push加载 ...
- 《Linux命令行与shell脚本编程大全》 第二十二章 学习笔记
第二十二章:使用其他shell 什么是dash shell Debian的dash shell是ash shell的直系后代,ash shell是Unix系统上原来地Bourne shell的简化版本 ...
- Http网络通信--网络图片查看
1.要在andorid中实现网络图片查看,涉及到用户隐私问题,所以要在AndroidManifest.xml中添加访问网络权限 <uses-permission android:name=&qu ...
- 使用Linux的mail命令发送邮件
由于经常工作在linux下,所以很多时候需要将自己工作的报告或其他有用的东东发送给相关的人,所以花时间研究了一下在linux下如何发送mail.我们通常能用到下面3中发送方式: 1. 使用Shell当 ...
- 为什么Wireshark无法解密HTTPS数据
为什么Wireshark无法解密HTTPS数据 导读 由于需要定位一个问题,在服务器上tcpdump抓取https数据包,然后下载到本地打开wireshark分析.然后我们下载域名私钥配置到wires ...
- Linux下mv命令详解
mv命令格式:mv [选项] 源文件或目录 目标文件或目录 mv命令参数(选项): -b :若需覆盖文件,则覆盖前先行备份. -f :force 强制的意思,如果目标文件已经存在,不会询问而直接覆盖: ...
- [golang学习] 在idea中code & debug
[已废弃]不需要看 idea 虽然审美倒退了n年. 不过功能还是相当好用的. idea 的go插件堪称最好的go ide. 1. 语法高亮支持 2. 智能提示 3. 跳转定义(反跳转回来) 4. 集成 ...