第一大块儿:读取通讯录


1、iOS 6以上系统,争取获取用户允许:

初始化的时候须要推断。设备是否授权
-(id)init{
self = [super init];
[self createdABHandle];
bool isAuthorized = [self isAuthorizedAddressBook];
if (!isAuthorized) {
ABAddressBookRequestAccessWithCompletion(_addressBook, ^(bool granted, CFErrorRef error){
if (granted){
//[self createAddressBuddyData];
}
});
}
return self;
}

看看isAuthorizedAddressBook的内容:

- (BOOL)isAuthorizedAddressBook {
if (SYSTEM_VERSION <= 6.0){
return YES;
}
//
ABAuthorizationStatus status = ABAddressBookGetAuthorizationStatus();
if (status == kABAuthorizationStatusNotDetermined) {
return NO;
}
else if(status == kABAuthorizationStatusAuthorized) {
return YES;
}
else if (status == kABAuthorizationStatusDenied) {
return NO;
}
return NO;
}

2、获取联系人。获取联系人分组

ABAddressBookRef addressBook =ABAddressBookCreate();
NSArray* allPeople = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople (addressBook));
NSArray* allGroups = CFBridgingRelease(ABAddressBookCopyArrayOfAllGroups(addressBook));
for (id person in (NSArray *) allPeople)
[self logContact:person];
for (id group in (NSArray *) allGroups)
[self logGroup:group];
CFRelease(addressBook);

3、联系人字段获取技巧:

● 获取个人或群体完整名称。

比如:NSString* name = (NSString*)ABRecordCopyCompositeName(record);

● 获取联系人ID ABRecordID recId = ABRecordGetRecordID(record);

● 获取电话。邮箱列表,生日等,多键值的方法 比如:ABMultiValueRef phoneNumbersArr = ABRecordCopyValue(record, kABPersonPhoneProperty);

● 获取联系人分组名称 CFStringRef name = ABRecordCopyValue(group,kABGroupNameProperty);

● 获取联系人分组ID   ABRecordID recId = ABRecordGetRecordID(group);

在获取多值的属性时候须要注意:获取地址的时候,多键值有嵌套。

代码例如以下:

NSArray *allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);

int i;
for (i = 0; i < [allPeople count]; i++) {
ABRecordRef record = [allPeople objectAtIndex:i]; ABMutableMultiValueRef multiValue = ABRecordCopyValue(record, kABPersonAddressProperty);
for(CFIndex i=0;i<ABMultiValueGetCount(multiValue);i++)
{
NSString* HomeLabel=(NSString*)ABMultiValueCopyLabelAtInde x(multiValue, i);
if([HomeLabel isEqualTo:@"_$!<Home>!$_"])
{
CFDictionaryRef dict = ABMultiValueCopyValueAtIndex(multiValueArr, i);
NSString* street =CFBridgingRelease( CFDictionaryGetValue(dict, kABPersonAddressStreetKey));
NSString* city =CFBridgingRelease( CFDictionaryGetValue(dict, kABPersonAddressCityKey));
NSString* country =CFBridgingRelease(CFDictionaryGetValue(dict, kABPersonAddressCountryKey));
CFRelease(dict); NSString *syntheticAddress = [NSString stringWithFormat:@"%@, %@, %@"
,(street?street:@"")
,(city?city:@"")
,(country?country:@"")];
}

不能直接调用ABMultiValueCopyValueAtIndex,然后强转成NSString*

4、删除联系人或者群组

ABAddressBookRemoveRecord(addressBook, people, NULL);

ABAddressBookSave(addressBook, NULL);

第二大块儿:写入通讯录

注意。在写入通讯录的时候,有些contact的phone,email,address是多个的。鉴于多线程的安全因素,在使用的时候都是获取拷贝。

/*
更新联系人,比方phoneTypePairArr为空,那么程序把一个空的ABMultiValueRef写入addressBook,达到删除的效果。
*/
-(BOOL) updateToAB:(ABRecordRef)person withContact:(RCSContact*)contact{
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, nil);
// 保存到联系人对象中,假设属性为nil,则表示要删除
//lastname
NSString* lastName = contact.lastName.length? contact.lastName:nil;
ABRecordSetValue(person, kABPersonLastNameProperty, (__bridge CFStringRef)lastName, NULL); //firstName
NSString* firstName = [contact.firstName length]? contact.firstName:nil;
ABRecordSetValue(person, kABPersonFirstNameProperty, (__bridge CFStringRef)firstName, NULL); //birthday
if ([contact.birthday length]) {
NSDateFormatter *inputFormatter = [[NSDateFormatter alloc] init];
[inputFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]];
[inputFormatter setDateFormat:@"yyyy-MM-dd"];
NSDate* inputDate = [inputFormatter dateFromString:contact.birthday];
ABRecordSetValue(person, kABPersonBirthdayProperty, (__bridge CFDateRef)inputDate, NULL);
}else{
ABRecordSetValue(person, kABPersonBirthdayProperty, NULL, NULL);
} //company name
NSString* companyName = [contact.companyName length]?contact.companyName:nil;
ABRecordSetValue(person, kABPersonOrganizationProperty, (__bridge CFStringRef)companyName, NULL); //company duty
NSString* companyDuty = [contact.companyDuty length]?contact.companyDuty:nil;
ABRecordSetValue(person, kABPersonJobTitleProperty, (__bridge CFStringRef)companyDuty, NULL);
// ABMultiValueRef相似是Objective-C中的NSMutableDictionary
ABMultiValueRef mv = ABMultiValueCreateMutable(kABMultiStringPropertyType);
NSArray* tmpArr = [NSArray arrayWithArray:contact.phoneTypePairArr];
for (PhoneTypePair* p in tmpArr) {
if ([p.content length]==0) {
continue;
}
NSString* label = [_typeDic_ForWrite objectForKey:p.type];
ABMultiValueIdentifier mi = ABMultiValueAddValueAndLabel(mv, (__bridge CFStringRef)p.content, (__bridge CFStringRef)label, &mi);
}
ABRecordSetValue(person, kABPersonPhoneProperty, mv, NULL);
if (mv) { CFRelease(mv);}
//设置邮箱
ABMutableMultiValueRef emailCFArray = ABMultiValueCreateMutable(kABStringPropertyType);
tmpArr = [NSArray arrayWithArray:contact.eMailArr];
for (PhoneTypePair* p in tmpArr) {
//邮件的话
if ([p.content length]==0) {
continue;
}
NSString* label = [_typeDic_ForWrite objectForKey:p.type];
ABMultiValueAddValueAndLabel(emailCFArray,(__bridge CFStringRef)(p.content),(__bridge CFStringRef)label,NULL);
}
ABRecordSetValue(person, kABPersonEmailProperty, emailCFArray, NULL);
if(emailCFArray){CFRelease(emailCFArray);} //设置地址
ABMutableMultiValueRef multiAddress = ABMultiValueCreateMutable(kABMultiDictionaryPropertyType);
tmpArr = [NSArray arrayWithArray:contact.addressArr];
for (PhoneTypePair* p in tmpArr) {
//地址的话
if ([p.content length]==0) {
continue;
}
NSMutableDictionary *addressDictionary = [[NSMutableDictionary alloc] init];
[addressDictionary setObject:[p.content mutableCopy] forKey:(NSString *)kABPersonAddressStreetKey];
[addressDictionary setObject:@"" forKey:(NSString *)kABPersonAddressCityKey];
[addressDictionary setObject:@"" forKey:(NSString *)kABPersonAddressStateKey];
[addressDictionary setObject:@"" forKey:(NSString *)kABPersonAddressZIPKey];
[addressDictionary setObject:@"" forKey:(NSString *)kABPersonAddressCountryCodeKey];
//set label
NSString* label = [_typeDic_ForWrite objectForKey:p.type];
ABMultiValueAddValueAndLabel(multiAddress, CFBridgingRetain(addressDictionary), (__bridge CFStringRef)label, NULL);
}
ABRecordSetValue(person, kABPersonAddressProperty, multiAddress, NULL);
if (multiAddress) {CFRelease(multiAddress);} // 设置头像属性
ABPersonRemoveImageData(person, NULL);
ABAddressBookAddRecord(addressBook, person, nil); //设置头像
NSData *data = UIImagePNGRepresentation(contact.portrait);
ABAddressBookSave(addressBook, NULL);
if ([data length]==0) {
ABPersonRemoveImageData(person, NULL);
}else{
CFDataRef cfData = CFDataCreate(NULL, [data bytes], [data length]);
ABPersonSetImageData(person, cfData, nil);
} // 将新建的联系人加入到通讯录中,保存通讯录
ABAddressBookAddRecord(addressBook, person, NULL);
bool isSucess = ABAddressBookSave(addressBook, NULL);
NSLog (@"加入一个人到数据库成功[%d]",isSucess);
// 释放通讯录对象的引用
if (addressBook) {
CFRelease(addressBook);
}
return isSucess;

比如:第35,47,61行,都是採用拷贝数组。防止多线程下读、写数组。导致enumed error

第三大块儿:监听通讯录变更

client代码须要这么实现:

/*
移除注冊函数
*/
-(void)dealloc{
ABAddressBookUnregisterExternalChangeCallback(_addressBook, ContactsChangeCallback, nil);
}
/*
注冊回调函数
*/
- (id)init {
self = [super init];
[self addressBookHandle];
ABAddressBookRegisterExternalChangeCallback(_addressBook, ContactsChangeCallback, nil); return self;
}
/*
回调函数,实现自己的逻辑。 */
void ContactsChangeCallback (ABAddressBookRef addressBook,
CFDictionaryRef info,
void *context){ NSLog(@"ContactsChangeCallback");
}

_addressBook是通讯录句柄。尽管有监听的接口,可是參数info总是空的。

另外:当本APP编辑系统通讯录时候。不会收到通知;通知可能有多个,这时候能够採取:“仅仅处理第一个通知,淹没后面的通知。

”。也能够採取信号量机制,对变更通知一个个在线程中处理。防止界面卡顿。

另外:在加入新的联系人的时候,使用ABNewPersonViewController。特别注意,一定要处理好它的代理:

    // Called when the user selects Save or Cancel. If the new person was saved, person will be
// a valid person that was saved into the Address Book. Otherwise, person will be NULL.
// It is up to the delegate to dismiss the view controller.
- (void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(ABRecordRef)person;

处理不好,造成APP崩溃。

我的处理是这种:

    [self.navigationController popToViewController:self animated:YES];

监听规则:

当App活跃(前台+后台保活期间)的时候,当通讯录改动的时候,会收到通知

当App不活跃的时候(挂起的时候)。App收不到通知;而是,当App到前台的时候收到延迟的通知。

生成vcard

/*************************************************
* @brief 获取通讯录联系列表的vcard
* @param 无
* @retun 无
*************************************************/
- (void)getLocalAddressBookvCard { CFArrayRef persons = ABAddressBookCopyArrayOfAllPeople(_addressBook);
CFDataRef vcards = (CFDataRef)ABPersonCreateVCardRepresentationWithPeople(persons); NSString *vcardString = [[NSString alloc] initWithData:(__bridge NSData *)vcards encoding:NSUTF8StringEncoding]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *folderPath = [paths objectAtIndex:0];
NSString *filePath = [folderPath stringByAppendingPathComponent:@"contacts.vcf"];
NSLog(@"path = %@",filePath); [vcardString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}

生成到document文件夹下的contacts.vcf文件

还能够用以下的办法。更适合生成单个crad

+(NSData*)exportContactsToVcard:(NSArray*)contacts
{
NSMutableArray *people = [NSMutableArray arrayWithCapacity:contacts.count];
ABAddressBookRef ab = ABAddressBookCreate();
for (Contact *contact in contacts)
{
ABRecordRef person = ABAddressBookGetPersonWithRecordID(ab,contact.contactId);
[people addObject:(__bridge id)person];
}
NSData *vCard = (__bridge NSData*)ABPersonCreateVCardRepresentationWithPeople((__bridge CFArrayRef) people);
return vCard;
}

从vcard生成person

ABAddressBookRef book = ABAddressBookCreate();
ABRecordRef defaultSource = ABAddressBookCopyDefaultSource(book);
CFArrayRef vCardPeople = ABPersonCreatePeopleInSourceWithVCardRepresentation(defaultSource, vCardData);
for (CFIndex index = 0; index < CFArrayGetCount(vCardPeople); index++) {
ABRecordRef person = CFArrayGetValueAtIndex(vCardPeople, index);
ABAddressBookAddRecord(book, person, NULL);
} CFRelease(vCardPeople);
CFRelease(defaultSource);
ABAddressBookSave(book, NULL);
CFRelease(book);

iOS 通讯录编程【总结】的更多相关文章

  1. iOS通讯录相关知识-浅析

    本文来自于:贞娃儿的博客  http://blog.sina.com.cn/zhenwawaer  在开发一些应用中,我们如果需要iPhone设备中的通讯录信息.或者,需要开发通讯录相关的一些功能.那 ...

  2. iOS网络编程模型

    iOS网络编程层次结构也分为三层: Cocoa层:NSURL,Bonjour,Game Kit,WebKit Core Foundation层:基于 C 的 CFNetwork 和 CFNetServ ...

  3. IOS网络编程——第三方类库

    IOS网络编程——第三方类库 目录 概述 ASIHttpRequest AFNetworking 其他 概述 ASIHttpRequest AFNetworking 其他

  4. iOS 6编程Cookbook(影印版)

    <iOS 6编程Cookbook(影印版)> 基本信息 原书名:iOS 6 Programming Cookbook 作者: Vandad Nahavandipoor 出版社:东南大学出版 ...

  5. iOS 并行编程:NSOperation Queues

    1 简介 1.1 功能        Operation Queue也是IOS的一种并行编程技术,类似Dispatch Queue可以帮助用户管理多线程.但是Operation Queue将任务封装在 ...

  6. IOS网络编程:HTTP

    IOS网络编程:HTTP HTTP定义了一种在服务器和客户端之间传递数据的途径. URL定义了一种唯一标示资源在网络中位置的途径. REQUESTS 和 RESPONSES: 客户端先建立一个TCP连 ...

  7. 深入浅出-iOS函数式编程的实现 && 响应式编程概念

    简介 本篇主要回顾一下--iOS函数式编程 && 响应式编程概念 ,如何一步步实现函数式编程的过程,对阅读Masonry && SnapKit源码有一定的帮助. 配图 ...

  8. [HMLY]11.iOS函数式编程的实现&&响应式编程概念

    简介 本篇主要回顾一下--iOS函数式编程 && 响应式编程概念 ,如何一步步实现函数式编程的过程,对阅读Masonry && SnapKit源码有一定的帮助. 作为一 ...

  9. iOS网络编程笔记——Socket编程

    一.什么是Socket通信: Socket是网络上的两个程序,通过一个双向的通信连接,实现数据的交换.这个双向连路的一端称为socket.socket通常用来实现客户方和服务方的连接.socket是T ...

随机推荐

  1. “国家队爷”杯液体战争AI比赛!!__SymenYang

    原帖 这两天一直在搞这个AI,提供的样例更本不是我的风格啊,看不懂更不会改... 所以我自己写了一个AI的平台,现在在不断的修改AI的策略,smart样例还是很容易过的,让line的行走速度变慢一点到 ...

  2. node.js安装及其环境配置

    nodejs: 实际上是采用google的chrome浏览器V8引擎,由C++编写的 本质上是一个javascript的运行环境 浏览器引擎可以解析js代码 nodejs可以解析js代码,没有浏览器端 ...

  3. Android 微信SDK图片分享(checkArgs fail, thumbData is invalid)

    微信官网给的Demo中.图片的分享例子他是这么描述的: String url = "http://pic2.nipic.com/20090506/1478953_125254084_2.jp ...

  4. Android icon尺寸

    Android icon尺寸   密度范围 切图比例(以mdpi为基准) 切图比例(以xxxhdpi为基准) 图标尺寸 外间距 ((图标尺寸-图片尺寸)/2) mdpi 120dpi~160dpi 1 ...

  5. 使用 RxJava 的正确姿势

    最近在使用 RxJava 时遇到了一些比较诡异的问题,排查的过程中做了点研究,发现平时大家的用法多多少少都有些问题.有些地方存在隐患,有些用法不够简练,我把这些问题简单做一下分类和总结,供大家参考. ...

  6. MYSQL 使用自定义表变量

    mysql  用户自定义表变量,ENGINE=MyISAM DEFAULT CHARSET=gb2312; 制定编码方式,防止乱码 DROP TABLE IF EXISTS p_temp; creat ...

  7. win32窗口映射(部分)

    先理解一下“窗口”与“视区”的概念.“窗口”是逻辑坐标下的矩形区域,“视区”是设备坐标系下的区域.根据“窗口”和“视区”的大小可以确定x方向和y方向的比例因子. 例子如下: VOID OnPaint( ...

  8. JS——放大镜

    放大镜: 1.比例系数要恒定:在系数为4的情况下,若原图是820*512,那么小图必须是205*128,放大镜若是50,原图显示区域必须200 2.计算鼠标在小图中的坐标 3.放大镜需要在鼠标中间位置 ...

  9. VC使用CryptoAPI计算MD5

    // md5.h #include <tchar.h> #include <wincrypt.h> // 计算Hash,成功返回0,失败返回GetLastError() // ...

  10. Android中Webview使用经验总结

    很早前就喜欢在Android中使用Webview组件结合JS来做应用,总结了一些不错的小经验,在这里持续更新,自己备忘,也希望给其他需要的同学一些参考- 1.添加权限 要用Webview,确认你在** ...