为了兼容iOS9以前与iOS9以后用到的通讯录fromework,做了这个封装,方便调用CNContactStore
和AddressBook
需要导入的头文件
//ios9之前导入的框架
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>
//ios9之后导入的框架
#import <Contacts/Contacts.h>
#import <ContactsUI/ContactsUI.h>
封装的.h文件
#import <Foundation/Foundation.h>
@class UIViewController;
extern NSString *const contactNameKey;/*!通讯录人名key*/
extern NSString *const contactPhoneKey;/*!通讯录手机号key*/
/**
选择通讯录联系人之后的回调
@param dic 回调返回的参数,key请参考contactNameKey,contactPhoneKey;
*/
typedef void(^SelectContactBackBlock)(NSDictionary *dic);
NS_ASSUME_NONNULL_BEGIN
@interface RainContactKit : NSObject
/**
请求通讯录的授权信息,并且得到授权状态
*/
- (BOOL)requestContactAuthorized;
/**
从一个VC打开通讯录选取功能
@param fromVc 打开通讯录的VC
@param contactBackBlock 选取某个通讯录人之后的回调
*/
- (void)openContactFromVc:(UIViewController *)fromVc andCompletion:(SelectContactBackBlock) contactBackBlock;
@end
NS_ASSUME_NONNULL_END
封装的.m文件
#import "RainContactKit.h"
//ios9之前导入的框架
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>
//ios9之后导入的框架
#import <Contacts/Contacts.h>
#import <ContactsUI/ContactsUI.h>
NSString *const contactNameKey = @"rainContactNameKey";
NSString *const contactPhoneKey = @"rainContactPhoneKey";
static NSString *const alertMessage = @"您没有开启访问通讯录的权限,是否前往设置打开本程序的通讯录权限";
@interface NSString (PhoneNumberTrim)
/**
过滤手机号里的特殊字符
*/
- (NSString *)trimPhoneNumber;
@end
@implementation NSString (PhoneNumberTrim)
- (NSString *)trimPhoneNumber {
return [[[self stringByReplacingOccurrencesOfString:@"-" withString:@""]
stringByReplacingOccurrencesOfString:@" " withString:@""]
stringByReplacingOccurrencesOfString:@"+86" withString:@""];
}
@end
@interface RainContactKit ()<CNContactPickerDelegate,ABPeoplePickerNavigationControllerDelegate>
@property (nonatomic,copy)SelectContactBackBlock contactBackBlock;
@end
@implementation RainContactKit
- (BOOL)requestContactAuthorized {
if (@available(iOS 9.0,*)) {
return [self requestContactAuthorizedAfterSystemVersionNine];
}else {
return [self requestContactAuthorizedBeforeSystemVersionNine];
}
}
#pragma mark- IOS9 及以后调用的方法
/**
IOS9 及以后调用的方法
*/
- (BOOL)requestContactAuthorizedAfterSystemVersionNine {
CNAuthorizationStatus contactStatus = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
NSString *authorizedMsg = @"";
__block BOOL isAuthorized = NO;
switch (contactStatus) {
case CNAuthorizationStatusNotDetermined: {
authorizedMsg = @"用户未确定是否授权";
CNContactStore *contactStore = [[CNContactStore alloc]init];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[contactStore requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (error) {
isAuthorized = NO;
#ifdef DEBUG
NSLog(@"contact 授权 error:%@",error.description);
#endif
}else if(granted == NO){
isAuthorized = NO;
}else {
isAuthorized = YES;
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
break;
case CNAuthorizationStatusRestricted:
authorizedMsg = @"iOS 设备上一些许可配置阻止程序与通讯录数据库进行交互";
isAuthorized = NO;
break;
case CNAuthorizationStatusDenied:
authorizedMsg = @"用户明确的拒绝了你的程序对通讯录的访问";
isAuthorized = NO;
break;
case CNAuthorizationStatusAuthorized:
authorizedMsg = @"用户已经授权给你的程序对通讯录进行访问";
isAuthorized = YES;
break;
default:
authorizedMsg = @"default 未知信息";
isAuthorized = NO;
break;
}
#ifdef DEBUG
NSLog(@"在iOS 9及以上设备上,通讯录授权信息:%@ %d",authorizedMsg,isAuthorized);
#endif
return isAuthorized;
}
- (void)presentContactAfterSystemVersionNineFromVc:(UIViewController *)fromeVc {
CNContactPickerViewController *contactVc = [[CNContactPickerViewController alloc]init];
contactVc.delegate = self;
[fromeVc presentViewController:contactVc animated:YES completion:nil];
}
#pragma mark- IOS9 以前调用的方法
/**
IOS9 以前调用的方法
*/
- (BOOL)requestContactAuthorizedBeforeSystemVersionNine {
ABAuthorizationStatus abStatus = ABAddressBookGetAuthorizationStatus();
NSString *authorizedMsg = @"";
__block BOOL isAuthorized = NO;
switch (abStatus) {
case kABAuthorizationStatusNotDetermined:{
authorizedMsg = @"用户未确定是否授权";
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(0);
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
if (granted) {
isAuthorized = YES;
}else {
isAuthorized = NO;
}
dispatch_semaphore_signal(semaphore2);
});
dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER);
CFRelease(addressBook);
}
break;
case kABAuthorizationStatusRestricted:
authorizedMsg = @"iOS 设备上一些许可配置阻止程序与通讯录数据库进行交互";
isAuthorized = NO;
break;
case kABAuthorizationStatusDenied:
authorizedMsg = @"用户明确的拒绝了你的程序对通讯录的访问";
isAuthorized = NO;
break;
case kABAuthorizationStatusAuthorized:
authorizedMsg = @"用户已经授权给你的程序对通讯录进行访问";
isAuthorized = YES;
break;
default:
authorizedMsg = @"default 未知信息";
isAuthorized = NO;
break;
}
#ifdef DEBUG
NSLog(@"在iOS 9以下设备上,通讯录授权信息:%@ %d",authorizedMsg,isAuthorized);
#endif
return isAuthorized;
}
- (void)presentContactBeforeSystemVersionNineFromVc:(UIViewController *)fromeVc {
ABPeoplePickerNavigationController *nav = [[ABPeoplePickerNavigationController alloc] init];
nav.peoplePickerDelegate = self;
[fromeVc presentViewController:nav animated:YES completion:nil];
}
#pragma mark- inner fucntion
- (void)openContactFromVc:(UIViewController *)fromVc andCompletion:(SelectContactBackBlock) contactBackBlock {
if ([self requestContactAuthorized]) {
self.contactBackBlock = contactBackBlock;
if (@available(iOS 9.0,*)) {
[self presentContactAfterSystemVersionNineFromVc:fromVc];
}else {
[self presentContactBeforeSystemVersionNineFromVc:fromVc];
}
}else {
[self alertShow];
}
}
- (void)alertShow {
UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"温馨提示" message:alertMessage preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *goAction = [UIAlertAction actionWithTitle:@"去设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[[UIApplication sharedApplication]openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
[alertVC addAction:goAction];
[alertVC addAction:cancelAction];
[UIApplication.sharedApplication.keyWindow.rootViewController presentViewController:alertVC animated:YES completion:nil];
}
#pragma mark- CNContactPickerDelegate
- (void)contactPickerDidCancel:(CNContactPickerViewController *)picker {
[picker dismissViewControllerAnimated:YES completion:nil];
}
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact {
NSString *givenName = contact.givenName;
NSString *familyName = contact.familyName;
//全名
NSMutableString *name = [NSMutableString string];
if ([familyName length] > 0) { [name appendString:familyName]; }
if ([givenName length] > 0) { [name appendString:givenName]; }
NSString *phone = nil;
NSArray *tmpArr = contact.phoneNumbers;
for (CNLabeledValue *labelValue in tmpArr) {
CNPhoneNumber * number = labelValue.value;
NSString *phoneNumber = number.stringValue;
if (phoneNumber.length > 0) {
phone = phoneNumber;
break;
}
}
phone = [phone trimPhoneNumber];
NSMutableDictionary *contactDic = @{}.mutableCopy;
[contactDic setValue:name.copy forKey:contactNameKey];
[contactDic setValue:phone forKey:contactPhoneKey];
[picker dismissViewControllerAnimated:YES completion:^{
if (self.contactBackBlock) {
self.contactBackBlock(contactDic.copy);
}
}];
}
#pragma mark ABPeoplePickerNavigationControllerDelegate
//取消选择
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker
{
[peoplePicker dismissViewControllerAnimated:YES completion:nil];
}
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person
{
CFTypeRef abFirstName = ABRecordCopyValue(person, kABPersonFirstNameProperty);//名
CFTypeRef abLastName = ABRecordCopyValue(person, kABPersonLastNameProperty);//姓
NSString *firstNameString = (__bridge NSString *)abFirstName;
NSString *lastNameString = (__bridge NSString *)abLastName;
//全名
NSMutableString *name = [NSMutableString string];
if ([lastNameString length] > 0) { [name appendString:lastNameString]; }
if ([firstNameString length] > 0) { [name appendString:firstNameString]; }
ABMultiValueRef phones= ABRecordCopyValue(person, kABPersonPhoneProperty);//手机号数组
NSString *phone = nil;
for (NSInteger j = 0; j < ABMultiValueGetCount(phones); j++) {
/*因在ARC模式下,__bridge_transfer会自动对core指针release,同时得到cocoa对象指针,所以不用针对core指针做CFRelease()*/
NSString *phoneNumber = (__bridge_transfer NSString *)(ABMultiValueCopyValueAtIndex(phones, j));
if (phoneNumber.length > 0) {
phone = phoneNumber.copy;
break;
}
}
phone = [phone trimPhoneNumber];
CFRelease(phones);
NSMutableDictionary *contactDic = @{}.mutableCopy;
[contactDic setValue:name.copy forKey:contactNameKey];
[contactDic setValue:phone forKey:contactPhoneKey];
CFRelease(abFirstName);
CFRelease(abLastName);
[peoplePicker dismissViewControllerAnimated:YES completion:^{
if (self.contactBackBlock) {
self.contactBackBlock(contactDic.copy);
}
}];
}
@end
解释说明
在等待用户授权通讯录的地方使用了信号量semaphore,目的是为了同步到用户的授权状态再反馈给使用者
点击获取的通讯录数据保存在字典里,使用的key分别为:
extern NSString *const contactNameKey;/*!通讯录人名key*/
extern NSString *const contactPhoneKey;/*!通讯录手机号key*/
需要配置plist文件
<key>NSContactsUsageDescription</key>
<string>方便您填写联系人</string>
网友评论