首页 > 开发 > iOS > 正文

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

2016-04-17 22:38:39  来源:极客头条

一、通讯录   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 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;