iOS通讯录整合,兼容iOS789写法,附demo

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保持一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<span style= "font-size: 13px; font-family: 宋体;" >    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 (@ "没有授权" );
         // 弹窗提示去获取权限
     }</span>

iOS9及以后调用方法改成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<span style= "font-size: 13px; font-family: 宋体;" >     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 (@ "没有授权" );
     }    </span>

 

二、弹出通讯录选择界面

iOS7的写法如下,代理方法的返回值大多是BOOL类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<span style= "font-size: 13px; font-family: 宋体;" >- ( 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 ;
}
</span>

 

iOS8的代理方法换了,改成了下面两个,但是方法内部的取值基本相同

1
2
3
4
5
6
<span style= "font-size: 13px; font-family: 宋体;" > // 点击了通讯录名字就会退出
- ( void )peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person;
 
// 点击了名字里面的电话或邮箱才会退出
- ( void )peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier;
</span>

至于会调用哪一个方法,可以根据实际需要去选择,在弹出界面的方法中predicateForSelectionOfPerson 这个属性传false就是调用下面的。

1
2
3
4
5
<span style= "font-size: 13px; font-family: 宋体;" >    ABPeoplePickerNavigationController *pickervc = [[ABPeoplePickerNavigationController alloc] init];
     pickervc.predicateForSelectionOfPerson = [ NSPredicate  predicateWithValue: false ];
     pickervc.peoplePickerDelegate =  self ;
     [target presentViewController:pickervc animated: YES  completion: nil ];
</span>

 

iOS9系统下的弹出选择器方法 和 代理方法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<span style= "font-size: 13px; font-family: 宋体;" // 弹出选择器  
- ( 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);
}
</span>

这个是点击了名字就直接回调的方法,如果希望点击了属性再回调,则需要加上这一行

1
2
3
4
5
<span style= "font-size: 13px; font-family: 宋体;" >contactVc.predicateForSelectionOfContact = [ NSPredicate  predicateWithValue: false ];
 
// 代理方法调用
- ( void )contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty
</span>

 

三、获取全部通讯录信息

关于批量获取所有通讯录信息的方法有点冗长,这里就不一一贴了,只贴下iOS9的写法,iOS7和8的代码demo里都有。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<span style= "font-size: 13px; font-family: 宋体;" >- ( 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);
         }
     }];
}
</span>

 

四、写入通讯录

因为写入的话这个功能有点重量级,写入的时候要写入,名字、电话、email、地址等等,这就会使得api过于复杂。暂时我见到过的做法大多都是如果用户给了通讯录权限 那就给你插入一条名字+电话,我做了只有这两个入参的api,当然使用时也完全可以扩展成更多参数的。

iOS7和8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<span style= "font-size: 13px; font-family: 宋体;" >- ( 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);
}
</span>

iOS9下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<span style= "font-size: 13px; font-family: 宋体;" >- ( 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 ];
}
</span>

 

五、我的demo

因为不同版本用的类和枚举都不一样,所以我要设置一个统一的,并且在我的manager中处理各个版本间的判断。 最后开放出来统一的api,只要引入头文件SXAddressBookManager.h 就可以使用这些通用接口了。

①检查当前状态,有两种api 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- ( void )checkStatus1
{
     SXAddressBookAuthStatus status = [[SXAddressBookManager manager]getAuthStatus];
     if  (status == kSXAddressBookAuthStatusNotDetermined) {
         [[SXAddressBookManager manager]askUserWithSuccess:^{
             NSLog (@ "点击同意" );
         } failure:^{
             NSLog (@ "点击拒绝" );
         }];
     } else  if  (status == kSXAddressBookAuthStatusAuthorized){
         NSLog (@ "已有权限" );
     } else {
         NSLog (@ "没有权限" );
     }
}
1
2
3
4
5
6
7
8
- ( void )checkStatus2
{
     [[SXAddressBookManager manager]checkStatusAndDoSomethingSuccess:^{
         NSLog (@ "已经有权限,做相关操作,可以做读取通讯录等操作" );
     } failure:^{
         NSLog (@ "未得到权限,做相关操作,可以做弹窗询问等操作" );
     }];
}

②弹出选择窗口,点击回调选中的信息

1
2
3
4
5
6
- ( void )touchesBegan:( NSSet <UITouch *> *)touches withEvent:(UIEvent *)event
{
     [[SXAddressBookManager manager]presentPageOnTarget: self  chooseAction:^(SXPersonInfoEntity *person) {
         NSLog (@ "%@---%@" ,person.fullname,person.phoneNumber);
     }];
}

③获得整个通讯录信息

1
self .personEntityArray = [[SXAddressBookManager manager]getPersonInfoArray];

④往通讯录写入一条信息

1
[[SXAddressBookManager manager]creatItemWithName:@ "雷克萨斯-北京咨询电话"  phone:@ "010-88657869" ];

demo的地址是

https://github.com/dsxNiubility/SXEasyAddressBook

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值