zoukankan      html  css  js  c++  java
  • iOS 通讯录编程【总结】

    第一大块儿:读取通讯录


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


  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 洗牌
    判断一个窗口是否被挂起(发WM_NULL消息,或者调用IsHungAppWindow API进行测试)
    线程天敌TerminateThread与SuspendThread
    Visual C++ 异常(Exception)常见问题 (原文标题:A Visual C++ Exception FAQ)
    阻止屏保运行、显示器和系统待机(使用SystemParametersInfo和SetThreadExecutionState两种办法)
    C语言编译全过程
    MSbuild 教程
    Mac OS X下环境搭建 Sublime Text 2 环境变量配置 开发工具配置Golang (Go语言)
    grunt实用总结
    DDD(领域驱动设计)应对具体业务场景,如何聚焦 Domain Model(领域模型)?
  • 原文地址:https://www.cnblogs.com/mthoutai/p/7105279.html
Copyright © 2011-2022 走看看