iOS9.0 系统通讯录

在iOS的9.0系统之后,系统的通讯录框架使用了Contacts/Contacts.hContactsUI/ContactsUI.h,替换了AddressBook/AddressBook.hAddressBookUI/AddressBookUI.h

一、系统联系人界面

下面就来简单介绍一下ContactsUI/ContactsUI.h的使用
1、ContactsUI/ContactsUI.h是系统自带UI的通讯录界面,我们不用去获取联系人数据,直接利用系统的UI界面就能够展示联系人界面。
2、首先导入头文件 #import <ContactsUI/ContactsUI.h>
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<CNContact*> *)contacts;
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperties:(NSArray<CNContactProperty*> *)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<CNContact*> *)contacts;
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperties:(NSArray<CNContactProperty*> *)contactProperties;

1、第一个方法会优先调用
2、只实现第一个方法,在点击Done按钮的时候则直接会dismiss掉联系人界面
3、只实现第二个方法,在点击Done按钮的时候则直接会dismiss掉联系人界面

// 只实现这个方法,只要点击done之后,就会被调用(选中联系人的时候或者没有选择的时候都会),联系人界面会自动dismiss掉
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContacts:(NSArray<CNContact*> *)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<CNContactProperty*> *)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);
    }
    
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容

  • 作者:gabriel theodoropoulos,原文链接,原文日期:2015-09-29译者:BridgeQ、...
    梁杰_numbbbbb阅读 1,530评论 0 17
  • 现在有很多应用都需要读取我们的联系人列表,比如,微信,QQ等等。还有些专门的通讯录应用,就可以不用官方自带的简陋的...
    磨砺营IT阅读 736评论 1 4
  • 打开简书时,点进一篇文章,读着读着眼眶红了,不为别的,泪奔涌而下,只为世间所有的凉薄。不知什么时候活的如此柔软,一...
    梅园遗珠阅读 417评论 0 2
  • 最近几天,因为美国学生过来交流,所以基本上每天都很忙。认识了一个小弟Dylan,虽然不是很帅,但很活泼,容易相处。...
    前方前方阅读 198评论 0 0