iOS学习笔记29-系统服务(二)通讯录

来源:互联网 发布:林允儿 爆料 知乎 编辑:程序博客网 时间:2024/05/16 14:52

一、通讯录

iOS中的通讯录是存储在数据库中的,由于iOS的权限设计,开发人员是不允许直接访问通讯录数据库的,实现通讯录操作需要使用到AddressBook.framework框架。

AddressBook.framework框架:
  • 可以从底层去操作通讯录的所有信息,做到精确控制
  • 是基于C语言编写的,无法使用ARC管理内存,需要开发者手动管理内存
  • 需要自构UI界面

iOS还提供了另外一个框架来供开发者操作通讯录,那就是AddressBookUI.framework

AddressBookUI.framework框架:
  • 该框架封装AddressBook.framework,向外提供现成视图控制器使用
  • 可以使用ARC管理内存
  • 高度封装化,界面固定,可定制性差

这两个框架各有各的优点,各有各的缺点,具体采用哪一种去操作通讯录看具体需求决定。

二、AddressBook

AddressBook.framework框架是基于C语言的,缺少面向对象的思想,所以我们可以把里面一些结构体理解为一个“类”

首先我们来了解几个核心结构体:
  1. ABAddressBookRef
    通讯录对象,全局管理通讯录操作,比如修改保存等
  2. ABRecordRef
    通用的记录对象,可以是一条联系人信息,也可以是一个群组,通过具体类型进行区分,每条记录都有一个唯一ID标识
  3. ABPersonRef
    联系人信息,不常用,可以用类型为kABPersonTypeABRecordRef代替。
  4. ABGroupRef
    群组信息,不常用,可以用类型为kABGroupTypeABRecordRef代替。
常使用到关于记录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 /* 错误信息 */);
通讯录使用步骤:
  1. 创建通讯录对象ABAddressBookRef
  2. 请求用户授权操作通讯录ABAddressBookRequestAccessWithCompletion
  3. 查询所有通讯录的记录ABAddressBookCopyArrayOfAllPeople
  4. 添加记录,删除记录,修改记录
  5. 修改通讯录后,记住要通讯录保存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这个框架就提供了现成的控制器视图供开发者使用,高度封装化。

下面是这个框架中提供的控制器视图:
  1. ABPersonViewController
    用于查看联系人信息(可设置编辑)。
    需要设置displayedPerson属性来设置要显示或编辑的联系人。
  2. ABNewPersonViewController
    用于新增联系人信息。
  3. ABUnknownPersonViewController
    用于显示一个未知联系人(尚未保存的联系人)信息。
    需要设置displayedPerson属性来设置要显示的未知联系人。
  4. 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




代码Demo点这里:LearnDemo里面的AddressBookDemo

如果有什么问题可以在下方评论区留言,我会积极响应的!O(∩_∩)O哈!
0 0
原创粉丝点击