第一大块儿:读取通讯录
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);
第二大块儿:写入通讯录
/* 更新联系人,比方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; }
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);