iOS学习笔记 系统服务(二)通讯录
2016-09-22 12:56
447 查看
一、通讯录
iOS中的通讯录是存储在数据库中的,由于iOS的权限设计,开发人员是不允许直接访问通讯录数据库的,实现通讯录操作需要使用到AddressBook.framework框架。
AddressBook.framework框架:
可以从底层去操作通讯录的所有信息,做到精确控制
是基于C语言编写的,无法使用ARC管理内存,需要开发者手动管理内存
需要自构UI界面
iOS还提供了另外一个框架来供开发者操作通讯录,那就是
AddressBookUI.framework
AddressBookUI.framework框架:
该框架封装
AddressBook.framework,向外提供现成视图控制器使用
可以使用ARC管理内存
高度封装化,界面固定,可定制性差
这两个框架各有各的优点,各有各的缺点,具体采用哪一种去操作通讯录看具体需求决定。
二、AddressBook
AddressBook.framework框架是基于C语言的,缺少面向对象的思想,所以我们可以把里面一些结构体理解为一个“类”
首先我们来了解几个核心结构体:
ABAddressBookRef:
通讯录对象,全局管理通讯录操作,比如修改保存等
ABRecordRef:
通用的记录对象,可以是一条联系人信息,也可以是一个群组,通过具体类型进行区分,每条记录都有一个唯一ID标识
ABPersonRef:
联系人信息,不常用,可以用类型为
kABPersonType的
ABRecordRef代替。
ABGroupRef:
群组信息,不常用,可以用类型为
kABGroupType的
ABRecordRef代替。
常使用到关于记录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 /* 错误信息 */ );
通讯录使用步骤:
创建通讯录对象
ABAddressBookRef
请求用户授权操作通讯录
ABAddressBookRequestAccessWithCompletion
查询所有通讯录的记录
ABAddressBookCopyArrayOfAllPeople
添加记录,删除记录,修改记录
修改通讯录后,记住要通讯录保存
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这个框架就提供了现成的控制器视图供开发者使用,高度封装化。
下面是这个框架中提供的控制器视图:
ABPersonViewController:
用于查看联系人信息(可设置编辑)。
需要设置
displayedPerson属性来设置要显示或编辑的联系人。
ABNewPersonViewController:
用于新增联系人信息。
ABUnknownPersonViewController:
用于显示一个未知联系人(尚未保存的联系人)信息。
需要设置
displayedPerson属性来设置要显示的未知联系人。
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
相关文章推荐
- iOS学习笔记29-系统服务(二)通讯录
- iOS学习笔记29-系统服务(二)通讯录
- iOS学习笔记29-系统服务(二)通讯录
- iOS学习笔记28-系统服务(一)短信和邮件
- iOS学习笔记28-系统服务(一)短信和邮件
- iOS学习笔记30-系统服务(三)蓝牙
- iOS学习笔记 系统服务(三)蓝牙
- iOS学习笔记30-系统服务(三)蓝牙
- iOS学习笔记30-系统服务(三)蓝牙
- iOS学习笔记 系统服务(一)短信和邮件
- iOS学习笔记28-系统服务(一)短信和邮件
- 【初学】iOS学习笔记2-iOS模拟器简体中文系统语言更改
- iOS开发系列--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook系统服务开发汇总
- 安卓学习笔记--通过ContentProvider读写系统的通讯录
- iOS开发系列--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook系统服务开发汇总
- iOS开发系列--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook系统服务开发汇总
- iOS学习笔记(三)——iOS系统架构
- 【iOS学习笔记】中将事件添加到系统日历
- iOS开发长文--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook系统服务开发汇总
- Linux 学习笔记 -- 第四部分 Linux 使用着管理 -- 第18章 认识系统服务