zoukankan      html  css  js  c++  java
  • 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

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

     

  • 相关阅读:
    solr
    2.配置Flutter代码编辑器(IDE)
    1.Flutter的下载安装和环境配置
    ReactNative开发环境配置,新手踩坑必备.我也是新手
    汉字转拼音,获取汉字首字母
    For循环性能优化
    JavaScript滑块简易取色器
    C# 获取汉字拼音首字母(修正X问题,真正修正)
    团队项目-个人博客5.31
    团队项目-个人博客5.30
  • 原文地址:https://www.cnblogs.com/dsxniubility/p/5541565.html
Copyright © 2011-2022 走看看