iOS 通讯录编程【总结】

来源:互联网 发布:人工智能技术及应用 编辑:程序博客网 时间:2024/04/24 23:54

第一大块儿:读取通讯录


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

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

客户端代码需要这么实现:

/* 移除注册函数 */-(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);


2 0