通讯录
参考文档
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);