mthoutai

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

第一大块儿:读取通讯录


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);


posted on 2017-07-02 10:19  mthoutai  阅读(338)  评论(0编辑  收藏  举报