博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS通讯录整合,兼容iOS789写法,附demo
阅读量:6294 次
发布时间:2019-06-22

本文共 9626 字,大约阅读时间需要 32 分钟。

苹果的通讯录功能在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
*)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的地址是

这里写了我说的那三点常用,如果以后有一些刚需,会不断补充。 董铂然博客园。

 

转载地址:http://cdvta.baihongyu.com/

你可能感兴趣的文章
spring batch中用到的表
查看>>
资源文件夹res/raw和assets的使用
查看>>
UINode扩展
查看>>
LINUX常用命令
查看>>
百度云盘demo
查看>>
概率论与数理统计习题
查看>>
初学structs2,简单配置
查看>>
Laravel5.0学习--01 入门
查看>>
时间戳解读
查看>>
sbin/hadoop-daemon.sh: line 165: /tmp/hadoop-hxsyl-journalnode.pid: Permission denied
查看>>
@RequestMapping 用法详解之地址映射
查看>>
254页PPT!这是一份写给NLP研究者的编程指南
查看>>
《Data Warehouse in Action》
查看>>
String 源码浅析(一)
查看>>
Spring Boot 最佳实践(三)模板引擎FreeMarker集成
查看>>
Fescar 发布 0.2.3 版本,支持 Redis 和 Apollo
查看>>
Google MapReduce到底解决什么问题?
查看>>
CCNP-6 OSPF试验2(BSCI)
查看>>
Excel 2013 全新的图表体验
查看>>
openstack 制作大于2TB根分区自动扩容的CENTOS镜像
查看>>