标签:
当开发iOS软件的时候,就与用户的移动生活建立了联系。用户走到哪里都带着移动设备,可以说移动设备与用户的个人生活(从日历到个人相册)休戚相关。在这种移动生活中,通讯录占据着重要的位置。应用可以使用联系人数据库确定用户是否有朋友注册了相关服务:分析联系人的电子邮件地址或电话号码,并将联系人自动添加为好友。应用还可以使用联系人列表自动填写邮件地址或电话号码,或让用户通过蓝牙与朋友分享联系人信息。应用需要访问用户通讯录的原因不胜枚举。
注意:
除非有充分的理由,否则不要访问联系人数据库,这很重要,因为没有什么比泄露隐私更能让用户毅然决然地放弃使用您的应用了。
想要使用地址簿框架,需要先将两个有关地址薄的框架导入到项目中:AddressBookUI.framework和AddressBook.framework。其中前者提供了选择、编辑和显示联系人的图形用户界面,而后者是让您能够与联系人数据交互。然后在相关的类中导入头文件,如下所示:
#import <AddressBook/AddressBook.h> #import <AddressBookUI/AddressBookUI.h>首先,我在项目中创建了两个类的属性:addressBook和addressBookEntryArray
@property (nonatomic, assign) ABAddressBookRef addressBook; @property (nonatomic, strong) NSMutableArray *addressBookEntryArray;
addressBookEntryArray是用来存储获取到的系统中的地址薄条目数组。
注意:
将地址薄复制到内存的开销极高,因此应最大限度地减少这种操作的次数。在使用地址簿的时候,我们应该获取访问手机中地址薄的权限:
//申请访问权限 ABAddressBookRequestAccessWithCompletion(_addressBook, ^(bool greanted, CFErrorRef error) { //greanted为YES是表示用户允许,否则为不允许 if (!greanted) { NSLog(@"未取得权限"); } });接下来,创建一个地址薄的引用:
CFErrorRef error = NULL; //创建通讯簿的引用 _addressBook = ABAddressBookCreateWithOptions(nil, &error);在创建地址薄引用的时候现在最好用ABAddressBookCreateWithOptions这个方法,原来的 ABAddressBookCreate已经作废。
AB_EXTERN ABAddressBookRef ABAddressBookCreateWithOptions(CFDictionaryRef options, CFErrorRef* error) __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_6_0); AB_EXTERN ABAddressBookRef ABAddressBookCreate(void) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_NA,__MAC_NA,__IPHONE_2_0,__IPHONE_6_0);由上面的代码可以看出, ABAddressBookCreate支持的版本是iOS 2.0 到 iOS 6.0,在开发iOS 6.0 之后系统的时候应该使用 ABAddressBookCreateWithOptions去创建地址薄的引用。
_addressBookEntryArray = (__bridge NSMutableArray *)ABAddressBookCopyArrayOfAllPeople(_addressBook);
创建了地址薄的引用后,就要将手机地址薄中的联系人拷贝到数组addressBookEntryArray中,这里,我拷贝了所有联系人的信息,其他的拷贝方式请自行查看API文档。在获取所有联系人之前需要考虑到地址薄为空的情形,这种情况下给用户一个良好的提示,让用户知道APP没有出错崩溃,这里我用了一个alert弹窗来简单的显示提示信息。
if(ABAddressBookGetPersonCount(_addressBook) == 0) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"联系人为空" message:@"" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles: nil]; [alertView show]; }在读到地址薄中联系人信息后,这里将获得的数据展示在了一个tableviewcontroller中。
三、从地址簿中读取数据
读入到addressBookEntryArray数组中的每一个条目都是一个ABRecordRef,单值数据可以用方法ABRecordCopyValue方法读取:
ABRecordRef record = (__bridge ABRecordRef)([_addressBookEntryArray objectAtIndex:indexPath.row]); CFStringRef firstName = ABRecordCopyValue(record, kABPersonFirstNameProperty); CFStringRef lastName = ABRecordCopyValue(record, kABPersonLastNameProperty);这里获取了联系人中的firstName和lastName。
ABRecordRef的所有单值属性如下表所示:
属性名 | 描述 |
kABPersonFirstNameProperty | 名 |
kABPersonLastNameProperty | 姓 |
kABPersonMiddleNameProperty | 中间名或所写 |
kABPersonPrefixProperty |
姓名前缀(Mr.、Ms.、Dr.) |
kABPersonSuffixProperty |
姓名后缀(MD、Jr、Sr.) |
kABPersonNicknameProperty |
别名 |
kABPersonFirstNamePhoneticProperty |
名字的读音 |
kABPersonLastNamePhoneticProperty |
姓的读音 |
kABPersonMiddleNamePhoneticProperty |
中间名读音 |
kABPersonOrganizationProperty |
公司或组织 |
kABPersonJobTitleProperty |
职务 |
kABPersonDepartmentProperty |
部门 |
kABPersonEmailProperty |
邮箱地址 |
kABPersonBirthdayProperty |
CFDate格式的生日,可自由桥接到NSDate |
kABPersonNoteProperty |
备注 |
kABPersonCreationDateProperty |
CFDate格式的创建日期 |
kABPersonModificationDateProperty |
CFDate格式的最后修改日期 |
当从地址薄中读取多值数据,例如:电话号码、电子邮件地址和街道地址这些数据时,需要用到ABMultiValueRef。这里展示了如何操作地址薄中的地址(地址是一个比较特殊的多值数据,因为取出来的是一个字典类型的数据):
// 通讯录地址的使用 ABMultiValueRef addressMulti = ABRecordCopyValue(record, kABPersonAddressProperty); NSString *address = nil; NSString *street = nil; NSString *city = nil; NSString *state = nil; NSString *zip = nil; if (ABMultiValueGetCount(addressMulti) > 0) { NSDictionary *addressDictionary = (__bridge NSDictionary *)(ABMultiValueCopyValueAtIndex(addressMulti, 0)); street = [addressDictionary objectForKey:(NSString *)kABPersonAddressStreetKey]; city = [addressDictionary objectForKey:(NSString *)kABPersonAddressCityKey]; state = [addressDictionary objectForKey:(NSString *)kABPersonAddressStateKey]; zip = [addressDictionary objectForKey:(NSString *)kABPersonAddressZIPKey]; } address = [NSString stringWithFormat: @"%@ %@ %@ %@", street, city, state, zip];从API中得知,地址是一个字典类型的值。首先将地址取出,然后在转换成字典,用API中对应的Key去获得对应的数据。
地址的组成部分由下表所示:
属性 | 描述 |
kABPersonAddressStreetKey |
街道名和门牌号(包括房间号) |
kABPersonAddressCityKey |
城市名 |
kABPersonAddressStateKey |
两字符州名或完整的州名 |
kABPersonAddressZIPKey |
邮政编码(5位或者9位数字) |
kABPersonAddressCountryKey |
完整的国家名 |
kABPersonAddressCountryCodeKey |
两字符国别码 |
在通过多值获取到电话号码后,使用的是电话号码的索引号。虽然电话号码索引对开发人员很有用,但是对用户来说毫无意义,因此这里使用地址标签获取到联系人使用的标签。要获取多值引用的标签,首先需要调用ABMultiValueCopyLabelAtIndex。调用这个函数时,使用的参数与获取多值对象的值时相同。这个函数返回一个未经本地化的字符串如:_$!<Mobile>!$_。虽然这个字符串比索引号有用的多,但还是不太合适显示给用户。为获得用户能看明白的字符串,需要将返回的标签进行本地化。为此,可调用ABAddressBookCopyLocalizedLabel,并将刚返回的CFStringRef作为参数。代码中我获取了电话号码的标签,并输出到后台:
// 读取多个值的方法 ABMultiValueRef phoneNumbers = ABRecordCopyValue(record, kABPersonPhoneProperty); if (ABMultiValueGetCount(phoneNumbers) > 0) { for (int i = 0; i < ABMultiValueGetCount(phoneNumbers); i++) { // 第二个参数是获取电话号码的类型,例如:手机,家庭电话等(地址簿标签) NSLog(@"%@ [%@]", ABMultiValueCopyValueAtIndex(phoneNumbers, i), ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(phoneNumbers, i))); } }
联系人选择器创建一个像系统内置的地址薄中的联系人选择器类似的一个界面。首先这个类需要遵守协议ABPeoplePickerNavigationControllerDelegate,然后使用如下的代码创建一个选择控制器。
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init]; picker.peoplePickerDelegate = self; [self presentViewController:picker animated:YES completion:nil];
选择器创建完成后,这里有三个委托方法,在用户与联系人选择器交互时做出相应。第一个方法是对用户“取消”按钮做出响应:
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker { [self dismissViewControllerAnimated:YES completion:nil]; }第二个委托方法是告诉联系人选择器,您不想让用户选择联系人的具体属性,即用户无法通过联系人选择器查看到联系人的详细信息。当用户选择一个联系人后,联系人选择器就会自动dismiss掉。
// 此方法选择联系人后,不能跳转到详细页面,即不能读取详细信息 - (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person { NSLog(@"You have selected: %@", ABRecordCopyValue(person, kABPersonFirstNameProperty)); }第三个委托方法实现后,用户可以查看联系人的详细信息。
// 可以获取联系人的详细信息 - (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier { NSLog(@"Person: %@, Property: %i, Identifier: %i", person, property, identifier); }
这里我们还可以定制联系人选择器进入详细页面后所能访问的内容,在创建联系人选择器后,加入下面的代码用户进入详细页面后只能查看联系人的电话号码:
picker.displayedProperties = [NSArray arrayWithObject: [NSNumber numberWithInt:kABPersonPhoneProperty]];
大多数的情况下,都使用内置的地址薄用户界面来显示或编辑联系人。在此代码中,将联系人信息显示在了一个tableview中,选中cell后将跳转到一个ABPersonViewController页面进行联系人详细信息的查看。
ABPersonViewController *personViewController = [[ABPersonViewController alloc] init]; ABRecordRef record = (__bridge ABRecordRef)([_addressBookEntryArray objectAtIndex:indexPath.row]); personViewController.personViewDelegate = self; personViewController.displayedPerson = record; personViewController.allowsEditing = YES; personViewController.allowsActions = NO; [self.navigationController pushViewController:personViewController animated:YES];
这里的allowsEditing属性,是设置进入详细页面后,是否能够编辑联系人信息。
ABNewPersonViewController *newPersonController = [[ABNewPersonViewController alloc] init]; newPersonController.newPersonViewDelegate = self; [self.navigationController pushViewController:newPersonController animated:YES];
当用户保存联系人时,会调用一个委托方法。在这个方法中,确定返回的联系人对象有效后,会依次调用ABAddressBookAddRecord和ABAddressBookSave将联系人保存到地址薄。
- (void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(ABRecordRef)person { if (person) { CFErrorRef error = NULL; ABAddressBookAddRecord(_addressBook, person, &error); ABAddressBookSave(_addressBook, &error); if (error != NULL) { NSLog(@"An error occurred"); } } _addressBookEntryArray = (__bridge NSMutableArray *)ABAddressBookCopyArrayOfAllPeople(_addressBook); [self.tableView reloadData]; [self.navigationController popToRootViewControllerAnimated:YES]; }
标签:
原文地址:http://my.oschina.net/are1OfBlog/blog/466617