iOS CNContactStore 与AddressBook

为了兼容iOS9以前与iOS9以后用到的通讯录fromework,做了这个封装,方便调用CNContactStoreAddressBook

需要导入的头文件

//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>

更新时间2018-12-4

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,458评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,030评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,879评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,278评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,296评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,019评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,633评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,541评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,068评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,181评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,318评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,991评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,670评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,183评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,302评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,655评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,327评论 2 358

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生_X自主阅读 15,982评论 3 119
  • 我坐在书架边的地上, 地上还有一些不知名的碎屑, 我瞬间感觉一些细小不可见的东西 在我周围升起,环抱着我! 我喜欢...
    开荒girl阅读 264评论 0 0
  • 大红纸铺开,一得阁的墨汁也已倒出,一团喜气,满室生香。 一切准备停当,就等着书法家协会的几位大家来送墨宝了。 腊月...
    九月流云阅读 968评论 31 24
  • 你的降临, 被赋予撬动历史的力量。 延绵的山脉, 你是一座奇峰。 力排众议,力扭乾坤, 使精神为之一振。 举孝廉,...
    逸楼居士阅读 192评论 0 1
  • 超能量的咖啡冥想助力每一个人实现心中的目标。是时候为今天的好种子浇水施肥晒太阳了[太阳] 我近期最想实现的一个目标...
    张蓉萍阅读 130评论 0 0