通讯录

参考文档
1.https://www.jianshu.com/p/69e6e17b6050
2.http://www.mamicode.com/info-detail-1516910.html
3.https://blog.csdn.net/zhouzhoujianquan/article/details/52218390
4.https://www.jianshu.com/p/0b3a9498d0a6
5.https://www.cnblogs.com/jiwangbujiu/p/5467302.html
6.XCode开发文档

通讯录框架

  • iOS9前的框架

    • AddressBookUI.framework框架
      • 带有UI
    • AddressBook.framework框架
      • 只是通讯录数据
  • iOS9后的框架

    • ContactsUI框架
      • 带有UI
    • Contacts框架
      • 只是通讯录数据

在调用用户数据时需要请求权限iOS9和iOS9框架不同, 需要区别

iOS9前

1.权限

// 判断当前的授权状态是否是用户还未选择的状态
- (void)authorizationStatus {
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
    ABAddressBookRef bookRef = ABAddressBookCreate();
    ABAddressBookRequestAccessWithCompletion(bookRef, ^(bool granted, CFErrorRef error) {
                if (granted) {
                    NSLog(@"授权成功!");
                } else {
                    NSLog(@"授权失败!");
                }
            });
    return ;
}
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
    NSLog(@"用户已授权");
} else {
NSLog(@"您的通讯录暂未允许访问,请去设置->隐私里面授权!");
}
   typedef CF_ENUM(CFIndex, ABAuthorizationStatus) {
    kABAuthorizationStatusNotDetermined = 0,    // deprecated, use CNAuthorizationStatusNotDetermined, 不确定
    kABAuthorizationStatusRestricted,           // deprecated, use CNAuthorizationStatusRestricted,设备上限制应用和通讯录数据交互
    kABAuthorizationStatusDenied,               // deprecated, use CNAuthorizationStatusDenied, 用户拒绝授权

    kABAuthorizationStatusAuthorized            // deprecated, use CNAuthorizationStatusAuthorized,已授权
} AB_DEPRECATED("use CNAuthorizationStatus");

2.AddressBookUI.framework

若是执行了peoplePickerNavigationController:didSelectPerson:就不会执行peoplePickerNavigationController:didSelectPerson:property:identifier:方法

- (void)showContacts {
    ABPeoplePickerNavigationController *pickerVC = [[ABPeoplePickerNavigationController alloc] init];
    pickerVC.peoplePickerDelegate = self;
    [self presentViewController:peoplePickerNav animated:YES completion:nil];
}
// 属性
@property(nonatomic,copy,nullable) NSArray<NSNumber*> *displayedProperties; // 设置在联系人详情页可见的属性
// 在详情列表展示电话属性
pickerVC.displayedProperties = @[[NSNumber numberWithInt:kABPersonPhoneProperty]];

@property(nonatomic,copy,nullable) NSPredicate *predicateForEnablingPerson NS_AVAILABLE_IOS(8_0);  // 默认是ture, 即所有person都可以点击
pickerVC.predicateForSelectionOfPerson = [NSPredicate predicateWithValue:false];  // 所有用户都不能被点击

// 代理 ABPeoplePickerNavigationControllerDelegate
// 选中某个联系人
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person NS_AVAILABLE_IOS(8_0) {
    ABMultiValueRef multiValuePhone = ABRecordCopyValue(person, kABPersonPhoneProperty);  // kABPersonPhoneProperty是person的一个电话属性
    CFIndex index = ABMultiValueGetCount(multiValuePhone);  // 获取电话的个数
    NSString *phoneNumber;
    NSString *phoneNumberLabel;
    for (CFIndex i = 0; i < index; i++) {
        // 电话号码也是 CFStringRef
        CFStringRef value = ABMultiValueCopyValueAtIndex(multiValuePhone, i); // 获取电话集合中的第i个的值
        CFStringRef label = ABMultiValueCopyLabelAtIndex(multiValuePhone, i); // 获取电话集合中的第i个的标签
        // 转成字符串
        phoneNumber = CFBridgingRelease(value);
        phoneNumberLabel = CFBridgingRelease(label);
        NSLog(@"phoneNumber = %@, phoneNumberLabel = %@", phoneNumber, phoneNumberLabel);
    }
    CFRelease(multiValuePhone);  // 释放对象,很重要,不然会造成内容泄漏
}

// Called after a property has been selected by the user.详情页
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier NS_AVAILABLE_IOS(8_0) {
    ABMultiValueRef multiValue = ABRecordCopyValue(person, property);
    CFIndex index = ABMultiValueGetIndexForIdentifier(multiValue, identifier);
    CFStringRef valueCF = ABMultiValueCopyValueAtIndex(multiValue, index);
    NSString *valueString = CFBridgingRelease(valueCF);
    CFRelease(multiValue);

    NSLog(@"=======%@", valueString);

}

// 点击取消按钮就会触发
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker;


// Deprecated, use predicateForSelectionOfPerson and/or -peoplePickerNavigationController:didSelectPerson: instead.
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person NS_DEPRECATED_IOS(2_0, 8_0);

// 下面方法已被舍弃
// Deprecated, use predicateForSelectionOfProperty and/or -peoplePickerNavigationController:didSelectPerson:property:identifier: instead.
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier NS_DEPRECATED_IOS(2_0, 8_0);

3.AddressBook.framework

iOS9后

1.权限 CNAuthorizationStatus

if (@available(iOS 9.0, *)) {
    // 获得通讯录的授权状态
    CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
    // 判断当前的授权状态是否是用户还未选择的状态
    if (status == CNAuthorizationStatusNotDetermined) {
            CNContactStore *store = [CNContactStore new];
            [store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {  // 若授权成功, granted为YES, error为nil; 若授权失败, grantd为NO, error不为nil;
                if (granted) {
                    NSLog(@"授权成功!");
                } else {
                    NSLog(@"授权失败!");
                }
            }];
    return ;
}

// 判断当前的授权状态是否为授权状态
if (status != CNAuthorizationStatusAuthorized) {
    NSLog(@"用户已授权");
} else {
    NSLog(@"您的通讯录暂未允许访问,请去设置->隐私里面授权!");
}

typedef NS_ENUM(NSInteger, CNAuthorizationStatus)
{
    CNAuthorizationStatusNotDetermined = 0, // 用户还未决定是否进行授权
    /*! The application is not authorized to access contact data.
     *  The user cannot change this application’s status, possibly due to active restrictions such as parental controls being in place. 设备上限制应用和通讯录数据交互 */
    CNAuthorizationStatusRestricted,
    CNAuthorizationStatusDenied,        // 用户拒绝授权
    CNAuthorizationStatusAuthorized     // 用户已授权
} NS_ENUM_AVAILABLE(10_11, 9_0);

2.ContactsUI框架

// 调用系统通讯录
- (void)showContacts {
    CNContactPickerViewController *pickerVC = [[CNContactPickerViewController alloc] init];
    pickerVC.delegate = self;
    [self presentViewController:pickerVC animated:YES completion:nil];
}
// 属性
// 1.
@property (nonatomic, copy, nullable) NSArray<NSString*> *displayedPropertyKeys; // 在详情页要展示的属性内容, 不设置默认显示所有内容
    pickerVC.displayedPropertyKeys = @[CNContactEmailAddressesKey];  // 展示关于邮箱的属性内容

// 2.
@property (nonatomic, copy, nullable) NSPredicate *predicateForEnablingContact; // 过滤出符合条件的对象使其可被选中, 不可选中的呈现灰色
    pickerVC.predicateForEnablingContact = [NSPredicate predicateWithFormat:@"emailAddresses.@count > 0"]; // 只有有邮箱的才可以被点击

// 3.
@property (nonatomic, copy, nullable) NSPredicate *predicateForSelectionOfContact; // 被点击的只有符合掉件的才会执行contactPicker:didSelectContact:方法, 页面dismiss; 不符合的执行)contactPicker:didSelectContactProperty:
    pickerVC.predicateForSelectionOfContact = [NSPredicate predicateWithFormat:@"emailAddresses.@count = 1"];  // 邮箱数量为1个

// 4.
@property (nonatomic, copy, nullable) NSPredicate *predicateForSelectionOfProperty; // 被点击的只有符合掉件的contactPicker:didSelectContactProperty:
方法, 页面dismiss; 不符合的进入对应的功能页面
    pickerVC.predicateForSelectionOfProperty = [NSPredicate predicateWithFormat:@"(key == 'emailAddresses') AND (value LIKE '*@qq163.com')"];  // 邮箱后缀为qq163.com的

// 代理方法 
NS_AVAILABLE_IOS(9_0)
@protocol CNContactPickerDelegate <NSObject>

@optional
// 点击取消按钮触发, 当控制器选择一个联系人的时候, 控制器会自动dismiss, 
- (void)contactPickerDidCancel:(CNContactPickerViewController *)picker;

// 选择单个联系人(predicateForSelectionOfContact不设置或者符合条件的联系人才会调用, 不符合不调用该方法并进入系统详情页)
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact {
    NSString *identifier = contact.identifier; // 在设备中的联系人的标识符是唯一的
    for (CNLabeledValue *value in contact.phoneNumbers) {
        CNPhoneNumber *phoneNumber = value.value;
        NSString *phoneNumberString = phoneNumber.stringValue;
        NSString *label = value.label;
        NSLog(@"===phoneNumber = %@, label = %@", phoneNumberString, label);

    }

    for (CNLabeledValue *value in contact.emailAddresses) {
        NSString *emailAddressString = value.value;
        NSString *label = value.label;
        NSLog(@"===emailAddressString = %@, label = %@", emailAddressString, label);
    }
}
// 选择某个联系人的某个属性, 比如邮箱属性、电话属性等;(predicateForSelectionOfProperty不设置或符合才会调用, 不符合不调用该方法并进入属性对应的功能页)
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty {
    CNContact *contact = contactProperty.contact;
}

// 实现该方法, 就可以多选联系人且和单选联系人不同, 不受predicateForSelectionOfContact影响
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContacts:(NSArray<CNContact*> *)contacts;

// 不设置或符合predicateForSelectionOfProperty的才会被返回;若不符合无法选中; 不选择返回nil; 
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperties:(NSArray<CNContactProperty*> *)contactProperties;

@end

3.几个类

  • CNContact
    // 联系人类
    // 几个常用属性
    @property (readonly, copy, NS_NONATOMIC_IOSONLY) NSArray<CNLabeledValue<CNPhoneNumber*>*>             *phoneNumbers;                   // 电话号码
    @property (readonly, copy, NS_NONATOMIC_IOSONLY) NSArray<CNLabeledValue<NSString*>*>                  *emailAddresses;                // 邮箱
    @property (readonly, copy, NS_NONATOMIC_IOSONLY) NSArray<CNLabeledValue<CNPostalAddress*>*>           *postalAddresses;                // 地址
    @property (readonly, copy, NS_NONATOMIC_IOSONLY) NSArray<CNLabeledValue<CNContactRelation*>*>         *contactRelations;              // 联系人    
    @property (readonly, copy, NS_NONATOMIC_IOSONLY) NSArray<CNLabeledValue<CNSocialProfile*>*>           *socialProfiles;                // 社交
    @property (readonly, copy, NS_NONATOMIC_IOSONLY) NSArray<CNLabeledValue<CNInstantMessageAddress*>*>   *instantMessageAddresses;      // 即时通讯(QQ等)
  • CNLabeledValue
    // 可以当成一个
    // 属性
    @property (readonly, copy, NS_NONATOMIC_IOSONLY) NSString *identifier; // 该标识符在设备的联系人中是唯一的

    @property (readonly, nullable, copy, NS_NONATOMIC_IOSONLY) NSString *label; // 标签
    @property (readonly, copy, NS_NONATOMIC_IOSONLY) ValueType value;    // 值

4.Contacts框架

Contact

1.增保存成功后会发出一个通知CNContactStoreDidChangeNotification

增加联系人数据步骤: 1.创建联系人->创建请求->联系人仓库执行请求

// 1.创建联系人
- (CNMutableContact *)createContact {
    CNMutableContact *contact = [[CNMutableContact alloc] init];
    // 姓
    contact.familyName = @"啊姓";
    // 名字
    contact.givenName = @"名";

    // 邮箱
    CNLabeledValue *homeEmailValue = [CNLabeledValue labeledValueWithLabel:CNLabelHome value:@"[email protected]"];
    CNLabeledValue *workEmailValue = [CNLabeledValue labeledValueWithLabel:CNLabelWork value:@"[email protected]"];
    contact.emailAddresses = @[homeEmailValue, workEmailValue];

    // 电话
    CNPhoneNumber *mobilePhoneNumber = [CNPhoneNumber phoneNumberWithStringValue:@"1234567890"];
    CNLabeledValue *mobilePhoneNumberValue = [CNLabeledValue labeledValueWithLabel:CNLabelPhoneNumberMobile value:mobilePhoneNumber];
    CNPhoneNumber *iPhonePhoneNumber = [CNPhoneNumber phoneNumberWithStringValue:@"0987654321"];
    CNLabeledValue *iPhonePhoneNumberValue = [CNLabeledValue labeledValueWithLabel:CNLabelPhoneNumberiPhone value:iPhonePhoneNumber];
    contact.phoneNumbers = @[mobilePhoneNumberValue, iPhonePhoneNumberValue];

    // 生日, 最少要设置day 和 month
    NSDateComponents *data = [[NSDateComponents alloc] init];
    data.day = 01;
    data.month = 03;
    data.year = 2003;
    contact.birthday = data;

    return contact;
}
// 2、3创建并执行
- (void)addContent:(CNMutableContact *)contact {
    // 创建请求
    CNSaveRequest *request = [[CNSaveRequest alloc] init];
    [request addContact:contact toContainerWithIdentifier:nil];

    // 仓库执行请求
    CNContactStore *store = [[CNContactStore alloc] init];
    BOOL isSuccess = [store executeSaveRequest:request error:nil];
    if (isSuccess) {
        NSLog(@"添加联系人成功");
    }
}

2.删

删除联系人数据步骤: 1.创建请求->联系人仓库执行请求
若deleteContact:传入的不是CNMutableContact类,会蹦

- (void)deleteContent:(CNMutableContact *)contact {
    CNMutableContact *newContact = contact;
    if ([contact isKindOfClass:[CNContact class]]) {
        newContact = [contact mutableCopy];
    }
    // 创建请求
    CNSaveRequest *request = [[CNSaveRequest alloc] init];
    [request deleteContact:newContact];

    // 仓库执行请求
    CNContactStore *store = [[CNContactStore alloc] init];
    BOOL isSuccess = [store executeSaveRequest:request error:nil];
    if (isSuccess) {
        NSLog(@"删除联系人成功");>>>
    }
}

3.改

修改联系人数据步骤: 1.创建请求->联系人仓库执行请求

- (void)updateContact:(CNMutableContact *)contact {
    CNMutableContact *newContact = contact;
    if ([contact isKindOfClass:[CNContact class]]) {
        newContact = [contact mutableCopy];
    }
    newContact.familyName = @"哈哈哈";
    // 创建请求
    CNSaveRequest *request = [[CNSaveRequest alloc] init];
    [request updateContact:newContact];

    // 仓库只听请求
    CNContactStore *store = [[CNContactStore alloc] init];
    BOOL isSuccess = [store executeSaveRequest:request error:nil];
    if (isSuccess) {
        NSLog(@"修改联系人成功");
    }
}

4.查

请求联系人数据步骤: 1.配置请求key->创建请求->联系人仓库执行请求
获取联系人具体某个数据时, 先判断是否有数据

// 1.获取所有的联系人
- (void)getAllContacts {
    CNContactStore *store = [[CNContactStore alloc] init];  // 联系人仓库
    NSArray *keysArray = @[CNContactPhoneNumbersKey, CNContactFamilyNameKey];    // 配置请求key
    NSArray *keysArray2 = @[[CNContactFormatter descriptorForRequiredKeysForStyle:CNContactFormatterStyleFullName]];  // 格式化全名
    CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:keysArray];    // 联系人仓库执行请求
    [store enumerateContactsWithFetchRequest:request error:nil usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {
        NSArray *phoneNumbersArray = contact.phoneNumbers;
        for (CNLabeledValue *value in phoneNumbersArray) {
            CNPhoneNumber *phoneNumber = value.value;
            NSString *label = value.label;
            NSString *phoneNumberString = phoneNumber.stringValue;
            NSLog(@"====%@的%@号码为:%@", contact.familyName, label, phoneNumberString);
        }
    }];
}

// 2.检索联系人
- (void)getMactchingContacts {
    CNContactStore *store = [[CNContactStore alloc] init];  // 联系人仓库
    NSPredicate *preficate = [CNContact predicateForContactsMatchingName:@""];  // 设置检索条件
    NSArray *contactArray =  [store unifiedContactsMatchingPredicate:preficate keysToFetch:@[CNContactGivenNameKey] error:nil];
    // preficate 是检索条件, keysToFetch设置需要联系人的某些数据
}

// 3.在取出CNContace的某些内容的时候, 先判断是否有内容, 若无内容情况下去获取会蹦
- (void)theJobTitleForContact:(CNContact *)contact {
    if ([contact isKeyAvailable:CNContactJobTitleKey]) {
    NSLog(@"====有CNContactJobTitleKey的值");
    }else {
    NSLog(@"====没有CNContactJobTitleKey的值");
    }
}

Group

// 添加组
- (void)addGroup:(CNMutableGroup *)group toContainerWithIdentifier:(nullable NSString *)identifier;
// 更新组
- (void)updateGroup:(CNMutableGroup *)group;
// 删除组
- (void)deleteGroup:(CNMutableGroup *)group;
// 组里添加成员
- (void)addMember:(CNContact *)contact toGroup:(CNGroup *)group;- (void)removeMember:(CNContact *)contact fromGroup:(CNGroup *)group;
// 组里添加子组
- (void)addSubgroup:(CNGroup *)subgroup toGroup:(CNGroup *)group NS_AVAILABLE(10_11, NA);
// 组里删除子组
- (void)removeSubgroup:(CNGroup *)subgroup fromGroup:(CNGroup *)group NS_AVAILABLE(10_11, NA);

results matching ""

    No results matching ""