iOS-封装系统通讯录调用

在项目中有的地方会用到获取通讯里面的某个联系人的联系方式,这里我们稍微封装一下这个方法。

第一步:我们需要获取通讯录权限

info.plist文件中,添加 key : Privacy - Contacts Usage Description,value:方便用户从通讯录选取要保存的号码,如下图:
在这里插入图片描述
第二步:新建通讯录管理类

1、新建一个ContactManager

添加两个Block,用于回调权限和联系人号码

/**
 * 通讯录权限Block
 *
 * @param isAuthorization 是否有权限;  YES:有权限;   NO:无权限
 */
typedef void(^AddressBookAuthorization)(BOOL isAuthorization);

/**
 * 通讯录号码Block
 *
 * @param phoneNumber 号码
 */
typedef void(^AddressBookOfPhoneNumberBlock)(NSString *phoneNumber);

添加两个方法,用于获取权限和弹出通讯录列表

/**
 * 模态弹出通讯录列表页
 *
 * @param addressBookOfPhoneNumber block
 */
- (void)presentAddressBookViewControllerWithPhoneNumber:(AddressBookOfPhoneNumberBlock)addressBookOfPhoneNumber;

/**
 * 获取通讯录权限方法,可以单独使用
 *
 * @param contactAuthorization block
 */
- (void)getAddressBookAuthorization:(AddressBookAuthorization)contactAuthorization;

2、获取通讯录权限问题

获取通讯录权限的时候,判断系统版本,当veision >= 9.0 时,使用CNAuthorizationStatus;否则使用ABAuthorizationStatus

- (void)getAddressBookAuthorization:(AddressBookAuthorization)contactAuthorization
{
    NSString *version = [UIDevice currentDevice].systemVersion;
    if (version.doubleValue >= 9.0) {
        CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
        if (status == CNAuthorizationStatusNotDetermined) {
            CNContactStore *store = [[CNContactStore alloc] init];
            [store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
                //第一次获取权限判断,点击 “不允许” 则 granted = NO;点击 “好” 则 granted = YES;
                contactAuthorization(granted);
            }];
        } else if (status == CNAuthorizationStatusAuthorized) {//有权限
            contactAuthorization(YES);
        } else {//无权限
            //NSLog(@"iOS 9 later 您未开启通讯录权限,请前往设置中心开启");
            contactAuthorization(NO);
        }
    } else {
        ABAddressBookRef bookref = ABAddressBookCreateWithOptions(NULL, NULL);
        ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
        if (status == kABAuthorizationStatusNotDetermined) {
            ABAddressBookRequestAccessWithCompletion(bookref, ^(bool granted, CFErrorRef error) {
                contactAuthorization(granted);
            });
        }
        if (status == kABAuthorizationStatusAuthorized) {
            contactAuthorization(YES);
        }else{
            //NSLog(@"iOS 9 before 您未开启通讯录权限,请前往设置中心开启");
            contactAuthorization(NO);
        }
    }
}

3、调起通讯录列表页

获取通讯录列表的时候,同样判断系统版本,当veision >= 9.0 时,使用CNContactPickerViewController;否则使用ABPeoplePickerNavigationController

- (void)presentAddressBookViewControllerWithPhoneNumber:(AddressBookOfPhoneNumberBlock)addressBookOfPhoneNumber
{
    [self getAddressBookAuthorization:^(BOOL isAuthorization) {
        if (isAuthorization) {
            [self presentABPeoplePickerOrCNContactPickerControllerWithPhoneNumber:addressBookOfPhoneNumber];
        } else {
            [self setContactAuthorization];
        }
    }];
}
- (void)presentABPeoplePickerOrCNContactPickerControllerWithPhoneNumber:(AddressBookOfPhoneNumberBlock)phoneNumberBlock
{
    NSString *version = [UIDevice currentDevice].systemVersion;
    //主线程刷新UI
    dispatch_async(dispatch_get_main_queue(), ^{
        
        self.phoneNumberBlock = phoneNumberBlock;
        
        if (version.doubleValue >= 9.0) {
            //iOS 9 之后
            CNContactPickerViewController *picker = [[CNContactPickerViewController alloc] init];
            picker.delegate = self;
            //picker.predicateForSelectionOfContact = [NSPredicate predicateWithFormat:@"emailAddresses.@count == 1"];
            //picker.predicateForEnablingContact = [NSPredicate predicateWithFormat:@"emailAddresses.@count > 0"];
            //picker.predicateForSelectionOfProperty = [NSPredicate predicateWithFormat:@"(key == 'emailAddresses') AND (value LIKE '*@mac.com')"];
            picker.displayedPropertyKeys = @[CNContactPhoneNumbersKey];//只显示手机号
            [KeyWidow.rootViewController presentViewController:picker animated:YES completion:nil];
        } else {
            //iOS 9 之前
            ABPeoplePickerNavigationController *picker = [ABPeoplePickerNavigationController new];
            picker.peoplePickerDelegate = self;
            picker.displayedProperties = @[[NSNumber numberWithInt:kABPersonPhoneProperty]];
            [KeyWidow.rootViewController presentViewController:picker animated:YES completion:nil];
        }
        
    });
}

4、代理方法回调

#pragma mark - CNContactPickerDelegate

- (void)contactPickerDidCancel:(CNContactPickerViewController *)picker
{
    [picker dismissViewControllerAnimated:YES completion:nil];
}

- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty;
{
    //注意:这里和displayedPropertyKeys属性设置相对应;只有 CNPhoneNumber 类型,如果添加别的类型,需要加类型判断,否则可能crash;例如:通讯录中的日期、邮箱...
    CNPhoneNumber *phoneNumber = contactProperty.value;
    NSString *phoneStr = phoneNumber.stringValue;

    //回调block,把号码带回去
    if (self.phoneNumberBlock) {
        self.phoneNumberBlock(phoneStr);
    }
}

#pragma mark - ABPeoplePickerNavigationControllerDelegate

- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{
    [peoplePicker dismissViewControllerAnimated:YES completion:nil];
}

// 当用户选中某一个联系人时会执行该方法,并且选中联系人后会直接退出控制器
//- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person{
//
//}
// 当用户选中某一个联系人的某一个属性时会执行该方法,并且选中属性后会退出控制器
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
    [self peoplePickerNavigationControllerDidCancel:peoplePicker];
    
    ABMultiValueRef phones = ABRecordCopyValue(person, property);
    CFIndex phoneCount = ABMultiValueGetCount(phones);
    NSString *phoneValue = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(phones, identifier);
    //回调block,把号码带回去
    if (self.phoneNumberBlock) {
        self.phoneNumberBlock(phoneValue);
    }
    CFRelease(phones);
}

5、处理未开通通讯录权限情况

- (void)setContactAuthorization
{
    //主线程刷新UI
    dispatch_async(dispatch_get_main_queue(), ^{
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"温馨提示" message:@"您没有开启通讯录权限,去开通权限!" preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"不了" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        }];
        UIAlertAction *action2 = [UIAlertAction actionWithTitle:@"这就去" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            if (@available(iOS 10.0, *)) {
                [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:^(BOOL success) {
                }];
            } else {
                // Fallback on earlier versions
                [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
            }
        }];

        [alert addAction:action1];
        [alert addAction:action2];
        [KeyWidow.rootViewController presentViewController:alert animated:YES completion:nil];
    });
}

6、外部使用该封装类

//在外部使用该封装类,需要强引用,否则对象会提前释放后,导致block提前释放,以至于无法回调
@property (nonatomic, strong) ContactManager *contactManager; 
self.contactManager = [[ContactManager alloc] init];
    [self.contactManager presentAddressBookViewControllerWithPhoneNumber:^(NSString * _Nonnull phoneNumber) {
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"%@",phoneNumber);
        });
    }];

也可以使用单例写法,这时候需要在ContactManager类中实现创建单例方法:

    [[ContactManager shareAddressBook] presentAddressBookViewControllerWithPhoneNumber:^(NSString * _Nonnull phoneNumber) {
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"%@",phoneNumber);
        });
    }];

7、Demo下载地址:https://github.com/MichaelSSY/ContactManager。

你可能感兴趣的:(iOS开发笔记,号码选择)