iOS通讯录整合,兼容iOS789写法,附demo
苹果的通讯录功能在iOS7,iOS8,iOS9 都有着一定的不同,iOS7和8用的是 <AddressBookUI/AddressBookUI.h> ,但是两个系统版本的代理方法有一些变化,有些代理方法都标注了 NS_DEPRECATED_IOS(2_0, 8_0) 并推荐了另一个代理方法与之对应。 而iOS8到iOS9则是直接弃用了<AddressBookUI/AddressBookUI.h>取而代之的是<ContactsUI/ContactsUI.h>,后者是OC调用,据说当时苹果宣布弃用AddressBookUI还引来了阵阵欢呼。这也就是在使用通讯录功能时得考虑版本各种判断,我也就是工作中遇到了这种坑,然后就顺手兼容封装了一下。希望能解决这个问题。
我觉得通讯录这里的类结构没必要像SDWebImage或是Core Location这样列出来详细去说。大家用到通讯录无外乎就三个功能:
1.点击弹出通讯录页面,选择了一个联系人的电话后直接将信息填到页面输入框内。
2.遍历所有的通讯录数据统一做批量操作,搭建新页面或直接上传。
3.给通讯录写入一条信息。
这里会先对比一下iOS789的写法,最后奉上demo(一个封装后的库,提供了非常便利的api)。不关心内部实现的朋友可以直接拉到demo部分。
一、首先是获取通讯录的权限
iOS7和8保持一致
ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
if (status == kABAuthorizationStatusNotDetermined) {
NSLog(@"还没问");
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error){
if(granted){
NSLog(@"点击同意");
}else{
NSLog(@"点击拒绝");
}
});
}else if (status == kABAuthorizationStatusAuthorized){
NSLog(@"已经授权");
[self loadPerson];
}else {
NSLog(@"没有授权");
// 弹窗提示去获取权限
}
iOS9及以后调用方法改成
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
if (status == CNAuthorizationStatusNotDetermined) {
[[[CNContactStore alloc]init] requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
NSLog(@"还没问");
if(granted){
NSLog(@"点击了同意");
[self loadPerson];
}else{
NSLog(@"点击了拒绝");
}
}];
}else if (status == CNAuthorizationStatusAuthorized){
NSLog(@已经授权");
}else {
NSLog(@"没有授权");
}
二、弹出通讯录选择界面
iOS7的写法如下,代理方法的返回值大多是BOOL类型。
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
return YES;
} - (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
ABMultiValueRef phone = ABRecordCopyValue(person, kABPersonPhoneProperty);
long index = ABMultiValueGetIndexForIdentifier(phone,identifier);
NSString *phoneNO = (__bridge NSString *)ABMultiValueCopyValueAtIndex(phone, index); CFStringRef lastName = ABRecordCopyValue(person, kABPersonLastNameProperty);
CFStringRef firstName = ABRecordCopyValue(person, kABPersonFirstNameProperty); NSString *lastname = (__bridge_transfer NSString *)(lastName);
NSString *firstname = (__bridge_transfer NSString *)(firstName); if (phone) {
[peoplePicker dismissViewControllerAnimated:YES completion:nil];
return NO;
}
return YES;
}
iOS8的代理方法换了,改成了下面两个,但是方法内部的取值基本相同
// 点击了通讯录名字就会退出
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person; // 点击了名字里面的电话或邮箱才会退出
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier;
至于会调用哪一个方法,可以根据实际需要去选择,在弹出界面的方法中predicateForSelectionOfPerson 这个属性传false就是调用下面的。
ABPeoplePickerNavigationController *pickervc = [[ABPeoplePickerNavigationController alloc] init];
pickervc.predicateForSelectionOfPerson = [NSPredicate predicateWithValue:false];
pickervc.peoplePickerDelegate = self;
[target presentViewController:pickervc animated:YES completion:nil];
iOS9系统下的弹出选择器方法 和 代理方法如下
// 弹出选择器
- (void)presentPageOnTarget{
CNContactPickerViewController *contactVc = [[CNContactPickerViewController alloc] init];
contactVc.delegate = self;
[target presentViewController:contactVc animated:YES completion:nil];
} // 代理方法
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact
{
SXPersonInfoEntity *personEntity = [SXPersonInfoEntity new];
NSString *lastname = contact.familyName;
NSString *firstname = contact.givenName;
NSLog(@"%@ %@", lastname, firstname);
personEntity.lastname = lastname;
personEntity.firstname = firstname; NSMutableString *fullname = [[NSString stringWithFormat:@"%@%@",lastname,firstname] mutableCopy];
[fullname replaceOccurrencesOfString:@"(null)" withString:@"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, fullname.length)];
personEntity.fullname = fullname; NSString *fullPhoneStr = [NSString string];
NSArray *phoneNums = contact.phoneNumbers;
for (CNLabeledValue *labeledValue in phoneNums) {
NSString *phoneLabel = labeledValue.label;
CNPhoneNumber *phoneNumer = labeledValue.value;
NSString *phoneValue = phoneNumer.stringValue;
NSLog(@"%@ %@", phoneLabel, phoneValue);
if (phoneValue.length > 0) {
fullPhoneStr = [fullPhoneStr stringByAppendingString:phoneValue];
fullPhoneStr = [fullPhoneStr stringByAppendingString:@","];
}
}
if (fullPhoneStr.length > 1) {
personEntity.phoneNumber = [fullPhoneStr substringToIndex:fullPhoneStr.length - 1];
}
self.chooseAction(personEntity);
}
这个是点击了名字就直接回调的方法,如果希望点击了属性再回调,则需要加上这一行
contactVc.predicateForSelectionOfContact = [NSPredicate predicateWithValue:false]; // 代理方法调用
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty
三、获取全部通讯录信息
关于批量获取所有通讯录信息的方法有点冗长,这里就不一一贴了,只贴下iOS9的写法,iOS7和8的代码demo里都有。
- (void)printAllPerson
{
// 获取
CNContactStore *contactStore = [[CNContactStore alloc] init];
NSArray *keys = @[CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey];
CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:keys]; // 遍历
[contactStore enumerateContactsWithFetchRequest:request error:nil usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {
NSString *lastname = contact.familyName;
NSString *firstname = contact.givenName;
NSLog(@"%@ %@", lastname, firstname);
NSArray *phoneNums = contact.phoneNumbers;
for (CNLabeledValue *labeledValue in phoneNums) {
NSString *phoneLabel = labeledValue.label;
CNPhoneNumber *phoneNumer = labeledValue.value;
NSString *phoneValue = phoneNumer.stringValue;
NSLog(@"%@ %@", phoneLabel, phoneValue);
}
}];
}
四、写入通讯录
因为写入的话这个功能有点重量级,写入的时候要写入,名字、电话、email、地址等等,这就会使得api过于复杂。暂时我见到过的做法大多都是如果用户给了通讯录权限 那就给你插入一条名字+电话,我做了只有这两个入参的api,当然使用时也完全可以扩展成更多参数的。
iOS7和8
- (void)creatItemWithName:(NSString *)name phone:(NSString *)phone
{
if((name.length < 1)||(phone.length < 1)){
NSLog(@"输入属性不能为空");
return;
}
CFErrorRef error = NULL; ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
ABRecordRef newRecord = ABPersonCreate();
ABRecordSetValue(newRecord, kABPersonFirstNameProperty, (__bridge CFTypeRef)name, &error); ABMutableMultiValueRef multi = ABMultiValueCreateMutable(kABMultiStringPropertyType);
ABMultiValueAddValueAndLabel(multi, (__bridge CFTypeRef)name, kABPersonPhoneMobileLabel, NULL); ABRecordSetValue(newRecord, kABPersonPhoneProperty, multi, &error);
CFRelease(multi); ABAddressBookAddRecord(addressBook, newRecord, &error); ABAddressBookSave(addressBook, &error);
CFRelease(newRecord);
CFRelease(addressBook);
}
iOS9下
- (void)creatItemWithName:(NSString *)name phone:(NSString *)phone
{
// 创建对象
// 这个里面可以添加多个电话,email,地址等等。 感觉使用率不高,只提供了最常用的属性:姓名+电话,需要时可以自行扩展。
CNMutableContact * contact = [[CNMutableContact alloc]init];
contact.givenName = name?:@"defaultname";
CNLabeledValue *phoneNumber = [CNLabeledValue labeledValueWithLabel:CNLabelPhoneNumberMobile value:[CNPhoneNumber phoneNumberWithStringValue:phone?:@"10086"]];
contact.phoneNumbers = @[phoneNumber]; // 把对象加到请求中
CNSaveRequest * saveRequest = [[CNSaveRequest alloc]init];
[saveRequest addContact:contact toContainerWithIdentifier:nil]; // 执行请求
CNContactStore * store = [[CNContactStore alloc]init];
[store executeSaveRequest:saveRequest error:nil];
}
五、我的demo
因为不同版本用的类和枚举都不一样,所以我要设置一个统一的,并且在我的manager中处理各个版本间的判断。 最后开放出来统一的api,只要引入头文件SXAddressBookManager.h 就可以使用这些通用接口了。
①检查当前状态,有两种api
- (void)checkStatus1
{
SXAddressBookAuthStatus status = [[SXAddressBookManager manager]getAuthStatus];
if (status == kSXAddressBookAuthStatusNotDetermined) {
[[SXAddressBookManager manager]askUserWithSuccess:^{
NSLog(@"点击同意");
} failure:^{
NSLog(@"点击拒绝");
}];
}else if (status == kSXAddressBookAuthStatusAuthorized){
NSLog(@"已有权限");
}else{
NSLog(@"没有权限");
}
}
- (void)checkStatus2
{
[[SXAddressBookManager manager]checkStatusAndDoSomethingSuccess:^{
NSLog(@"已经有权限,做相关操作,可以做读取通讯录等操作");
} failure:^{
NSLog(@"未得到权限,做相关操作,可以做弹窗询问等操作");
}];
}
②弹出选择窗口,点击回调选中的信息
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[[SXAddressBookManager manager]presentPageOnTarget:self chooseAction:^(SXPersonInfoEntity *person) {
NSLog(@"%@---%@",person.fullname,person.phoneNumber);
}];
}
③获得整个通讯录信息
self.personEntityArray = [[SXAddressBookManager manager]getPersonInfoArray];
④往通讯录写入一条信息
[[SXAddressBookManager manager]creatItemWithName:@"雷克萨斯-北京咨询电话" phone:@"010-88657869"];
demo的地址是
https://github.com/dsxNiubility/SXEasyAddressBook
这里写了我说的那三点常用,如果以后有一些刚需,会不断补充。 董铂然博客园。
iOS通讯录整合,兼容iOS789写法,附demo的更多相关文章
- mybatis学习笔记(五) -- maven+spring+mybatis从零开始搭建整合详细过程(附demo和搭建过程遇到的问题解决方法)
文章介绍结构一览 一.使用maven创建web项目 1.新建maven项目 2.修改jre版本 3.修改Project Facts,生成WebContent文件夾 4.将WebContent下的两个文 ...
- 【转】IOS AutoLayout详解(三)用代码实现(附Demo下载)
转载自:blog.csdn.net/hello_hwc IOS SDK详解 前言: 在开发的过程中,有时候创建View没办法通过Storyboard来进行,又需要AutoLayout,这时候用代码创建 ...
- Vue插件编写、用法详解(附demo)
Vue插件编写.用法详解(附demo) 1.概述 简单来说,插件就是指对Vue的功能的增强或补充. 比如说,让你在每个单页面的组件里,都可以调用某个方法,或者共享使用某个变量,或者在某个方法之前执行一 ...
- vue双向数据绑定原理探究(附demo)
昨天被导师叫去研究了一下vue的双向数据绑定原理...本来以为原理的东西都非常高深,没想到vue的双向绑定真的很好理解啊...自己动手写了一个. 传送门 双向绑定的思想 双向数据绑定的思想就是数据层与 ...
- C#开发微信公众平台-就这么简单(附Demo)转载
C#开发微信公众平台-就这么简单(附Demo) 来源:https://www.cnblogs.com/xishuai/p/3625859.html#!comments 写在前面 阅读目录: 服务号和 ...
- 移动端开发ios和安卓兼容问题
移动端开发ios和安卓兼容问题 最近做移动端混合开的时候遇到一些安卓和iOS的兼容性问题,兼容想问题不仅在浏览器存在也在APP开发当中也会经常遇到这样的情况. 最近看了一下内容很不错的移动端开发相关的 ...
- 排坑·IPhone&IOS中不兼容正则中的断言匹配
阅文时长 | 1.14分钟 字数统计 | 1834.4字符 主要内容 | 1.问题切入 2.什么是断言匹配 3.断言匹配的替换方案 4.声明与参考资料 『排坑·IPhone&IOS中不兼容正则 ...
- 开源分享:谷歌大佬联合打造《高级Kotlin强化实战(附Demo)》
Kotlin 以其简洁的特性而闻名,而在我们的实践中,更加简洁就意味着更加高效.事实上,在使用 Kotlin 的专业 Android 开发者中,有多达 67% 的人表示 Kotlin 已经帮助他们提升 ...
- fetch ios低版本兼容cannot clone a disturbed response
报错信息 ios 11以下 cannot clone a disturbed response github.com/github/fetc- 问题发生场景 使用了一个或者多个三方库 三方库或者自己的 ...
随机推荐
- 进程管理三大扩展工具htop
三大进程管理监控工具 HTOP 介绍: Htop是一款运行于Linux系统监控与进程管理软件,htop提供所有进程的列表,并且使用彩色标识出处理器.swap和内存状态.用户一般可以在top无法提供详尽 ...
- 深入学习jQuery样式操作
× 目录 [1]设置样式 [2]增加样式 [3]删除样式[4]切换样式[5]判断样式[6]样式操作 前面的话 使用javascript脚本化CSS是一个系列,包括行间样式.计算样式.CSS类.样式表. ...
- 使用Windows EFS(怎么给文件夹加密)进行文件加密
和Windows BitLocker一样,Encrypting File System(EFS,加密文件系统)是Windows内置的一套基于公共密钥的加密机制,可以加密NTFS分区上的文件和文件夹,能 ...
- 关于CLR、CIL、CTS、CLS、CLI、BCL和FCL 的区分与总结
关于CLR.CIL.CTS.CLS.CLI.BCL和FCL 的区分与总结 如果要想深入学习.NET平台,那么标题中的这些关键字对你来说并不陌生,这些名词构成了.NET庞大的生态系统,为了宏观认识.NE ...
- 整合struts2+hibernate详细配置步骤及注意事项
刚刚学完这两个框架,就迫不及待的做了一个例子,在整合两个框架的时候,也碰到了一些小问题,下面介绍一下配置的步骤: 1.创建一个自定义的struts2和hibernate的类库 因为之前写例子都是直接将 ...
- svn 几个常用命令(持续更新)
1:获取某个版本号(3583)下的代码 svn co http://tech.yoai.com:8300/c ...
- Sql Server之使用T_SQL创建,修改,查看数据库信息
一.使用Transact_SQL创建数据库 Transact_SQL语法如下: create database database_name [ on [primary] [<fi ...
- tomcat源码剖析系列
一个简单的web服务器 一个简单的servlet容器 连接器 创建httpRequest 创建HttpResponse 容器 生命周期 日志记录器 载入器 Session管理 关闭钩子 启动tomca ...
- Winform应用程序实现通用消息窗口
记得我之前发表过一篇文章<Winform应用程序实现通用遮罩层>,是实现了透明遮罩的消息窗口,功能侧重点在动图显示+消息提醒,效果看上去比较的炫,而本篇我又来重新设计通用消息窗口,功能重点 ...
- VS编译链接时错误(Error Link2005)的解决方法
近期参与的项目中使用了公司另外一个同事提供的一个静态库文件.该静态库文件集成了CUDA, OpenCL两个库,用于做图形加速计算,提高视频解码拼接速度.但是在编译链接项目时,VS爆出如下错误: 1&g ...