在iOS的9.0系统之后,系统的通讯录框架使用了
Contacts/Contacts.h
,ContactsUI/ContactsUI.h
,替换了AddressBook/AddressBook.h
,AddressBookUI/AddressBookUI.h
。
一、系统联系人界面
下面就来简单介绍一下ContactsUI/ContactsUI.h的使用
1、ContactsUI/ContactsUI.h是系统自带UI的通讯录界面,我们不用去获取联系人数据,直接利用系统的UI界面就能够展示联系人界面。
2、首先导入头文件 #import
3、展示界面
// 1.创建通讯录界面(自带系统UI界面)
// 真机或者模拟器的系统版本必须是9.0以上
CNContactPickerViewController *picketVC = [[CNContactPickerViewController alloc] init];
// 2.遵守代理 CNContactPickerDelegate
picketVC.delegate = self;
// 3.展现通讯录界面
[self presentViewController:picketVC animated:YES completion:nil];
5、代理方法有5个,iOS9之后系统支持多选和单选2中情况:
// 取消代理方法
- (void)contactPickerDidCancel:(CNContactPickerViewController *)picker;
// 单选代理方法
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact;
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty;
// 多选代理方法
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContacts:(NSArray *)contacts;
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperties:(NSArray *)contactProperties;
6、带附属属性展示界面
CNContactPickerViewController控制器有几个属性值:分别是
(1)displayedPropertyKeys,
(2)predicateForEnablingContact,
(3)predicateForSelectionOfContact,
(4)predicateForSelectionOfProperty
展示界面代码创建如下:
// 1.创建通讯录界面(自带系统UI界面),真机或者模拟器的系统版本必须是9.0以上
CNContactPickerViewController *picketVC = [[CNContactPickerViewController alloc] init];
// 2.遵守代理 CNContactPickerDelegate
picketVC.delegate = self;
// (1)picketVC.displayedPropertyKeys属性 进入联系人详情界面时需要展示的信息(必须在展示界面之前设置), displayedPropertyKeys不设置的话,会展示所有信息,如果设置的话,则只会根据数组中的信息进行展示,但是这个属性只适用于单选的状态,多选状态下不起作用。
picketVC.displayedPropertyKeys = @[CNContactGivenNameKey,CNContactPhoneNumbersKey,CNContactEmailAddressesKey];
// (2)picketVC.predicateForSelectionOfContact属性 用于控制联系人选中后的操作
picketVC.predicateForSelectionOfContact = [NSPredicate predicateWithFormat:@"emailAddresses.@count == 1"];
// (3)picketVC.predicateForSelectionOfProperty属性 用于控制联系人属性选中后的操作
picketVC.predicateForSelectionOfProperty = [NSPredicate predicateWithFormat:@"(key == 'emailAddresses') AND (value LIKE '*@mac.com')"]; // 也可以是@qq.com
// (4)picketVC.predicateForEnablingContact属性 用于控制联系人是否可以选择,如果不设置,默认所有的联系人都是可以交互的(可选中),如果设置了并且命中,则联系人不可交互,不命中的话,可以交互。此属性在多选和单选情况下都起作用
picketVC.predicateForEnablingContact = [NSPredicate predicateWithFormat:@"emailAddresses.@count > 0"];
// 3.展现通讯录界面
[self presentViewController:picketVC animated:YES completion:nil];
属性是有NSPredicate谓词类创建的,这个可以根据CNContactPickerViewController中属性的表述进行创建,比如NSPredicate *predicateForSelectionOfContact; // e.g. emailAddresses.@count == 1,emailAddresses.@count == 1就是描述字段
7、代理方法的实现
(1)取消代理方法
//点击cancel按钮时被调用(多选状态时,在Done按钮的左侧,单选,多选点击cancel按钮时,都会被调用)
- (void)contactPickerDidCancel:(CNContactPickerViewController *)picker{
NSLog(@"%s",__func__);
}
下面的4个代理方法只需要实现其中的一个就可以,分为单选(一次只能选中一个联系人),多选(同时选中多个联系人)2中情况,如果2种状态同时实现,则多选的优先级比较高,即首先调用多选联系人界面
(2)单选代理方法
// 如果单选状态下的2个方法同时实现,即实现
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact;
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty;
1、第一个方法会优先调用
2、只实现第一个方法,点击某一个联系人,则直接会dismiss掉联系人界面
3、只实现第二个方法,点击某一个联系人会直接进入到联系人详情界面,并且受displayedPropertyKeys属性约束
#pragma mark - 单选
// 只实现这个方法,选中某一个联系人的时候调用,在选中联系人后,联系人界面自动dismiss掉
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact{
NSLog(@"%s",__func__);
// 如果picker.predicateForSelectionOfContact没有设置,在选中联系人后,联系人界面自动dismiss掉,此方法会被调用
// 如果picker.predicateForSelectionOfContact有设置,并且命中了,联系人界面自动dismiss掉,此方法会被调用,如果没有命中,此方法不会被调用,则进入到联系人详情界面(联系人详情界面受displayedPropertyKeys属性约束)
// 在这里,我选择进入到联系人详情界面,并且详情界面的展示信息不受displayedPropertyKeys属性约束,在选中某一个属性的时候,就会调用系统的操作,比如点击了电话号码,则会直接拨打电话
// CNContactViewController *vc = [CNContactViewController viewControllerForContact:contact];
// [self.navigationController pushViewController:vc animated:YES];
}
// 只实现这个方法,点击某一个联系人会直接进入到联系人详情界面,并且受displayedPropertyKeys属性约束,在联系人详情界面中,选中某一个属性的时候调用,在选中属性后,详情界面会自动dismiss掉
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty{
// 如果picker.predicateForSelectionOfProperty没有设置,在选中联系人后,联系人界面自动dismiss掉,此方法会被调用
// 如果picker.predicateForSelectionOfProperty有设置,并且命中了,联系人详情界面自动dismiss掉,此方法会被调用,如果没有命中,则此方法不会被调用,联系人详情界面不会dismiss掉(联系人详情界面受displayedPropertyKeys属性约束)
//在选中某一个属性的时候,如果在方法中不做任何操作的话,则直接会dismiss掉,比如点击了电话号码,则会直接把控制器dismiss,而不是去拨打电话(predicateForSelectionOfProperty没有设置或者设置并命中)。
NSLog(@"%s",__func__);
// contactProperty.key(选中属性的键名),contactProperty.value(选中属性的属性,可以根据属性类型进行各种操作)
NSLog(@"%@,%@",contactProperty.key,contactProperty.value);
}
(3)多选代理方法
#pragma mark - 多选
// 如果多选状态下的2个方法同时实现,即实现
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContacts:(NSArray *)contacts;
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperties:(NSArray *)contactProperties;
1、第一个方法会优先调用
2、只实现第一个方法,在点击Done按钮的时候则直接会dismiss掉联系人界面
3、只实现第二个方法,在点击Done按钮的时候则直接会dismiss掉联系人界面
// 只实现这个方法,只要点击done之后,就会被调用(选中联系人的时候或者没有选择的时候都会),联系人界面会自动dismiss掉
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContacts:(NSArray *)contacts{
// predicateForSelectionOfContact,predicateForSelectionOfProperty属性 不影响
NSLog(@"%s,%lu",__func__,contacts.count);
// CNContactViewController *vc = [CNContactViewController viewControllerForContact:[contacts lastObject]];
// [self.navigationController pushViewController:vc animated:YES];
}
// 只实现这个方法,选中联系人的时候被调用(可以多选)点击done之后,联系人界面会自动dismiss掉
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperties:(NSArray *)contactProperties{
NSLog(@"%s",__func__);
NSLog(@"%@,%@",[contactProperties lastObject].key,[contactProperties lastObject].value);
// picker.predicateForSelectionOfContact属性 不影响
// 如果picker.predicateForSelectionOfProperty没有设置,所有的联系人都可以被选中
// 如果picker.predicateForSelectionOfProperty有设置,并且命中了,联系人能被选中,不命中的话不能被选中
}
在除了取消代理方法外的其余4个方法,只要实现一个就可以了,如果同时实现多选和单选代理方法,则多选代理的优先级比较高,如果同时实现单选(多选)代理方法,则选择联系人的方法优先级高。
二、获取系统联系人全部数据
/** 读取联系人 */
- (void)readContact {
if (@available(iOS 9.0, *)) {
// 获取指定的字段,并不是要获取所有字段,需要指定具体的字段
NSArray *keyToFetch = @[CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey];
CNContactFetchRequest *fetchRequest = [[CNContactFetchRequest alloc] initWithKeysToFetch:keyToFetch];
CNContactStore *contactStore = [[CNContactStore alloc] init];
[contactStore enumerateContactsWithFetchRequest:fetchRequest error:nil usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {
NSLog(@"---------------------------------");
NSString *givename = contact.givenName;
NSString *familyname = contact.familyName;
NSLog(@"%@%@", familyname, givename);
NSString *name = [NSString stringWithFormat:@"%@%@", contact.familyName, contact.givenName];
NSArray *phoneArray = contact.phoneNumbers;
CNPhoneNumber *phone = [[CNPhoneNumber alloc] initWithStringValue:@"leo"];
for (CNLabeledValue *labelvalue in phoneArray) {
phone = labelvalue.value;
NSLog(@"phone:%@", phone.stringValue);
}
[self.contactDic setObject:phone.stringValue forKey:name];
}];
} else {
// Fallback on earlier versions
// 创建通讯录对象
ABAddressBookRef bookRef = ABAddressBookCreate();
// 获取通讯录中所有的联系人
CFArrayRef arrayRef = ABAddressBookCopyArrayOfAllPeople(bookRef);
// 遍历所有联系人
CFIndex count = CFArrayGetCount(arrayRef);
for (int i = 0; i < count; i++)
{
ABRecordRef record = CFArrayGetValueAtIndex(arrayRef, i);
// 获取姓名
NSString *firstName = (__bridge_transfer NSString *)ABRecordCopyValue(record, kABPersonFirstNameProperty);
NSString *lastName = (__bridge_transfer NSString *)ABRecordCopyValue(record, kABPersonLastNameProperty);
NSLog(@"firstName = %@, lastName = %@", firstName, lastName);
// 获取电话号码
ABMultiValueRef multiValue = ABRecordCopyValue(record, kABPersonPhoneProperty);
CFIndex count = ABMultiValueGetCount(multiValue);
for (int i = 0; i < count; i ++)
{
NSString *label = (__bridge_transfer NSString *)ABMultiValueCopyLabelAtIndex(multiValue, i);
NSString *phone = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(multiValue, i);
NSLog(@"label = %@, phone = %@", label, phone);
}
CFRelease(multiValue);
}
CFRelease(bookRef);
CFRelease(arrayRef);
}
}
/** 写入联系人 */
- (void)writeContact {
if (@available(iOS 9.0, *)) {
// 创建对象
// 这个里面可以添加多个电话,email,地址等等。 感觉使用率不高,只提供了最常用的属性:姓名+电话,需要时可以自行扩展。
CNMutableContact *contact = [[CNMutableContact alloc] init];
contact.givenName = @"leo";
CNLabeledValue *phone = [CNLabeledValue labeledValueWithLabel:CNLabelPhoneNumberMobile value:[CNPhoneNumber phoneNumberWithStringValue:@"10086"]];
contact.phoneNumbers = @[phone];
// 把对象加到请求中
CNSaveRequest *request = [[CNSaveRequest alloc] init];
[request addContact:contact toContainerWithIdentifier:nil];
// 执行请求
CNContactStore *store = [[CNContactStore alloc] init];
[store executeSaveRequest:request error:nil];
} else {
// Fallback on earlier versions
NSString *name = @"ileo";
NSString *phone = @"10087";
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)phone, kABPersonPhoneMobileLabel, NULL);
ABRecordSetValue(newRecord, kABPersonPhoneProperty, multi, &error);
CFRelease(multi);
ABAddressBookAddRecord(addressBook, newRecord, &error);
ABAddressBookSave(addressBook, &error);
CFRelease(newRecord);
CFRelease(addressBook);
}
}