AddressBook.framework引导

作者: Stark_Dylan | 来源:发表于2016-12-30 17:58 被阅读578次

    title: AddressBook.framework
    date: 2016-08-22 17:44:14
    tags: Frameworks
    thumbnail: http://www.51wendang.com/pic/00a226f3c9fa6ed45d1d781c/1-817-png_6_0_0_300_110_360_270_892.5_1263-1200-0-0-1200.jpg


    AddressBook.framework/AddressBookUI.framework

    9.0之后, AddressBook.frameworkContacts.framework代替。但是目前大部分的应用软件起支撑的版本是iOS6.0或7.0, 所以AddressBook还大有用处。之前在简书的文章AddressBook, AddressBookUI中有提及, 但是由于是转载, 所以代码不是很清晰, 而且有一些读者希望得到清晰的代码以及详细的功能解释, 所以在这里把AddressBook.framework以及AddressBookUI.framework重新做一下详细的使用方法介绍。

    这篇文章先介绍AddressBook.frameworkAddressBookUI以及Contacts.framework也会补上。

    开始使用AdressBook

    首先, 导入我们需要的Framework

    #import <AddressBook/AddressBook.h>
    

    经常使用到:

    • ABAddressBookRef: 通讯录引用
    • ABRecordRef: 记录引用
    • ABPropertyID: 记录的属性ID

    获取AddressBook使用权限

    像使用相机、推送一样, 访问AddressBook同样需要获取权限。

    在获取权限之前,我们需要创建一个AddressBook的引用,用来做后续操作。

    static ABAddressBookRef r;
    
    static inline ABAddressBookRef getAddressBookRef() {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            CFErrorRef errorRef;
            r = ABAddressBookCreateWithOptions(NULL, &errorRef);
            if ( errorRef ) {
                ILog(@"%@", (__bridge NSString *)CFErrorCopyFailureReason(errorRef));
            }
        });
        return r;
    }
    

    ABAddressBookRef,作为后续使用的通讯录引用,这里写作单例。或者可以写作单例的属性,但只需初始化一次。

    ABAddressBookCreateWithOptions通过参数创建, options暂时为预留字段,但是AddressBook在9.0被弃用,估计不会被使用了。

    在获取到引用之后,我们应先查询AddressBook的访问状态:

    Boolean needRequestAccess() {
        ABAuthorizationStatus s = ABAddressBookGetAuthorizationStatus();
        if ( s == kABAuthorizationStatusDenied ||
            s == kABAuthorizationStatusRestricted ) {
                alert(@"提示", @"");
            return false;
        }
        if ( s != kABAuthorizationStatusAuthorized ) {
            return true;
        }
        return false;
    }
    

    推荐使用switch-case来判断状态。如果结果为kABAuthorizationStatusAuthorized表示可以正常访问。要注意的是,如果用户拒绝了首次的请求,那么需要用户在设置-隐私-通讯录中手动打开App使用通讯录的权限。

    如果结果为kABAuthorizationStatusNotDetermined意味着我们需要请求访问权限:

    Restricted意味着系统决定了访问权限,用户不能修改。

    void requestAddressBookAccess (ABAddressBookRequestAccessCompletionHandler handler) {
        ABAddressBookRequestAccessWithCompletion(getAddressBookRef(), handler);
    }
    

    综合起来的调用:

    void initAddressBook (void (^shouldAccessAddressBook)(Boolean boo)) {
        if ( needRequestAccess() ) {
            requestAddressBookAccess(^(bool granted, CFErrorRef error) {
                if ( granted ) {
                    shouldAccess = true;
                }
                shouldAccessAddressBook(granted);
            });
        } else {
            shouldAccess = true;
            shouldAccessAddressBook(true);
        }
    }
    

    非常简单的,我们获得了通讯录的访问权限。

    用户的查询

    用户的查询非常简单;

    • ABAddressBookGetPersonCount:获取总人数
    • ABAddressBookGetPersonWithRecordID:通过RecordID获取单个人,recordID可以通过ABRecordGetRecordID获得
    • ABAddressBookCopyArrayOfAllPeople:获取全部联系人数组
    • ABAddressBookCopyArrayOfAllPeopleInSource:获取记录引用中所有的练习嗯
    • ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering:带着排序参数
    • ABAddressBookCopyPeopleWithName:拷贝符合该名称的联系人

    获取联系人的数量:

    CFIndex i = ABAddressBookGetPersonCount(getAddressBookRef());
    printf("AddressBook: has %ld Person", i);
    

    获取联系人:

    CFArrayRef ref = ABAddressBookCopyArrayOfAllPeople(getAddressBookRef());
        
    ABRecordRef pr = CFArrayGetValueAtIndex(ref, 0);
    ILog(@"%@", ABRecordCopyValue(pr, kABPersonFirstNameProperty));
    

    获取联系人组使用ABAddressBookCopyArrayOfAllGroups即可。然后通过ABAddressBookCopyArrayOfAllPeopleInSource即可获得组内联系人。

    用户信息的修改与删除

    CFArrayRef ref = ABAddressBookCopyArrayOfAllPeople(getAddressBookRef());
        
    ABRecordRef pr = CFArrayGetValueAtIndex(ref, 0);
    ILog(@"%@", ABRecordCopyValue(pr, kABPersonFirstNameProperty));
        
    if ( ABRecordSetValue(pr, kABPersonFirstNameProperty, (__bridge CFStringRef)@"Hello", nil) ) {
        if ( ABAddressBookSave(getAddressBookRef(), nil) ) {
            ILog(@"Succeed!");
        }
    }
        
    if ( ABAddressBookRemoveRecord(getAddressBookRef(), pr, nil) ) {
        if ( ABAddressBookSave(getAddressBookRef(), nil) ) {
            ILog(@"Remove Succeed!");
        }
    }
    

    ABRecordCopyValue用来通过ABPropertyID获取相应属性的内容, 具体的ID在ABPerson.h中有详细列表。

    ABRecordSetValue用来通过ABPropertyID设置相应的属性内容,同时返回bool值以供判段。参数中的error已经不在使用。

    在修改后,切记保存修改,使用ABAddressBookSave做保存,在这之前,可以使用ABAddressBookHasUnsavedChanges判断是否存在未保存的修改。

    使用ABAddressBookRemoveRecord来移除记录。

    监听其他应用对AddressBook的修改

    在操作AddressBook的同时,有可能在后台的时候被其他程序所修改,addressBook提供了监听方法:

    void callBack(ABAddressBookRef addressBook, CFDictionaryRef info, void *context)  {
        ILog(@"AddressBook has changed in another application.");
    };
    
    void handleChange() {
        ABAddressBookRegisterExternalChangeCallback(getAddressBookRef(), callBack, nil);
    }
    

    在收到监听后我们需要做相应的处理,比如: 是否其他改动与我们的改动有重叠等。

    注意点

    • 在设置值的时候,可以使用ABMultiValueRef设置多个值,比如说多个电话。
    • 在不使用的时候,要使用ABAddressBookUnregisterExternalChangeCallback取消对AddressBook的监听。

    Q&A:

    Q:如何在删除完联系人的多个电话后,直接删除联系人?

    A:通过propertyID获取到电话的信息后,做简单的判断就可以实现。如果仅存这一条电话记录,那么在删除的同时也删除掉联系人即可。

    本文全部的代码

    #import "Ins_AddressBook.h"
    
    @implementation InsAddressBook
    
    static Boolean shouldAccess;
    static ABAddressBookRef r;
    
    static inline ABAddressBookRef getAddressBookRef() {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            CFErrorRef errorRef;
            r = ABAddressBookCreateWithOptions(NULL, &errorRef);
            if ( errorRef ) {
                ILog(@"%@", (__bridge NSString *)CFErrorCopyFailureReason(errorRef));
            }
        });
        return r;
    }
    
    void requestAddressBookAccess (ABAddressBookRequestAccessCompletionHandler handler) {
        ABAddressBookRequestAccessWithCompletion(getAddressBookRef(), handler);
    }
    
    Boolean needRequestAccess() {
        ABAuthorizationStatus s = ABAddressBookGetAuthorizationStatus();
        if ( s == kABAuthorizationStatusDenied ||
            s == kABAuthorizationStatusRestricted ) {
                alert(@"提示", @"你之前已经拒绝了程序的访问权限, 请在设置-隐私-通讯录中手动打开, 并重新启动应用。");
            return false;
        }
        if ( s != kABAuthorizationStatusAuthorized ) {
            return true;
        }
        return false;
    }
    
    void initAddressBook (void (^shouldAccessAddressBook)(Boolean boo)) {
        if ( needRequestAccess() ) {
            requestAddressBookAccess(^(bool granted, CFErrorRef error) {
                if ( granted ) {
                    shouldAccess = true;
                }
                shouldAccessAddressBook(granted);
            });
        } else {
            shouldAccess = true;
            shouldAccessAddressBook(true);
        }
    }
    
    void callBack(ABAddressBookRef addressBook, CFDictionaryRef info, void *context)  {
        ILog(@"AddressBook has changed in another application.");
    };
    
    void handleChange() {
        ABAddressBookRegisterExternalChangeCallback(getAddressBookRef(), callBack, nil);
    }
    
    void getPerson () {
        CFIndex i = ABAddressBookGetPersonCount(getAddressBookRef());
        printf("AddressBook: has %ld Person", i);
        CFArrayRef ref = ABAddressBookCopyArrayOfAllPeople(getAddressBookRef());
        
        ABRecordRef pr = CFArrayGetValueAtIndex(ref, 0);
        ILog(@"%@", ABRecordCopyValue(pr, kABPersonFirstNameProperty));
        
        if ( ABRecordSetValue(pr, kABPersonFirstNameProperty, (__bridge CFStringRef)@"Hello", nil) ) {
            if ( ABAddressBookSave(getAddressBookRef(), nil) ) {
                ILog(@"Succeed!");
            }
        }
        
        if ( ABAddressBookRemoveRecord(getAddressBookRef(), pr, nil) ) {
            if ( ABAddressBookSave(getAddressBookRef(), nil) ) {
                ILog(@"Remove Succeed!");
            }
        }
    }
    
    - (instancetype)init {
        self = [super init];
        if ( self ) {
            initAddressBook(^(Boolean boo){
                if ( boo ) {
                    ILog(@"Get access to addressBook")
                    handleChange();
                    getPerson();
                } else {
                    ILog(@"Without access to addressBook")
                }
            });
        }
        return self;
    }
    
    DEF_SINGLETON_AUTOLOAD(InsAddressBook)
    
    @end
    

    AddressBookUI.framework

    相比而言,使用UI则简单的多,直接进入创建一个新的用户:

    ABNewPersonViewController * pv = [[ABNewPersonViewController alloc] init];
    pv.newPersonViewDelegate = self;
    [getRootNc() presentViewController:INS_NAV(pv) animated:YES completion:nil];
    
    - (void) newPersonViewController: (ABNewPersonViewController *) newPersonView
            didCompleteWithNewPerson: (ABRecordRef) person {
        [newPersonView.navigationController dismissViewControllerAnimated:YES completion:nil];
    }
    

    创建界面与其代理方法,当然在ABNewPersonViewController中,有写参数我们可以设置,意义很简单,可以通过我们上边AddressBook.framework中获得的一些引用穿进去,或者在创建用户之前直接设置一个用户的基础信息。这里不做赘述。

    选择一个用户

    ABPeoplePickerNavigationController * pv = [[ABPeoplePickerNavigationController alloc] init];
    pv.peoplePickerDelegate = self;
    [getRootNc() presentViewController:pv animated:YES completion:nil];
    
    - (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person {
        
    }
    
    // Called after the user has pressed cancel.
    - (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker {
        [peoplePicker dismissViewControllerAnimated:YES completion:nil];
    }
    

    注:这里我没有低版本的测试环境,但是可以看到,选中人的方法在8.0后才被使用,所以低版本的童鞋应该主动尝试原本被弃用的2个方法。

    展示用户

    ABPersonViewController

    If displayedPerson has been added to an ABAddressBook, then the addressBook property will be updated to use the displayedPerson's ABAddressBook.

    可以设置的属性包括:允许编辑、允许操作(短信、邮件等),允许展示连接的联系人,设置属性高亮等。也比较方便。

    信息的完善

    ABUnknownPersonViewController

    ABUnknownPersonViewController *un = [[ABUnknownPersonViewController alloc] init];
    un.displayedPerson = person; // 展示的联系人
    un.allowsAddingToAddressBook = YES; // 允许添加到通讯录中
    [getRootNc() presentViewController:INS_NAV(un) animated:YES completion:nil];
    

    总结

    AddressBook.framework、AdressBookUI.framework还是可以满足一些基本需求,但是由于是c库,并且功能不是很完善,所以在iOS9.0之后苹果使用Contacts.framework来代替AddressBook.framework。ContactsFramework是一整套OC的库,理解起来也很简单。

    这是ContactsFramework中包含的一些头文件,在使用AddressBook的时候,基本所有的方法都在后边写了使用ContactsFramework中什么方法来代替。

    CNContact以及CNGroup分别代表了联系人与联系人组,比之前的Record引用清晰了许多。

    CNContactFetchRequest以及CNSaveRequest方便的提供了查询以及保存等操作。

    CNSaveRequest则提供了方便直观的方法去保存用户。

    然后整个框架都清晰了很多,基本的使用方法如下:

    CNContactStore * c;
    CNAuthorizationStatus s;
    CNContactFetchRequest * f;
    NSError * e;
        
    s = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
    c = [[CNContactStore alloc] init];
        
    if ( s != CNAuthorizationStatusAuthorized ) {
            ILog(@"Un Authorized !");
            
            [c requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * error) {
                
            }];
    }
        
        // 根据`CNContact`中的属性单独获得,比如 @[CNContactGivenNameKey, CNContactMiddleNameKey, ...]
    f = [[CNContactFetchRequest alloc] initWithKeysToFetch:@[CNContactMiddleNameKey, CNContactEmailAddressesKey, CNContactPhoneNumbersKey]];
        
    BOOL b = [c enumerateContactsWithFetchRequest:f error:&e usingBlock:^(CNContact * contact, BOOL * stop) {
            
            /*
             <CNContact: 0x7f8f4fb86dc0: identifier=41592D45-CE20-44C8-95C5-C1FE464474A5:ABPerson, givenName=(not fetched), familyName=(not fetched), organizationName=(not fetched), phoneNumbers=(
             "<CNLabeledValue: 0x7f8f4fb88dc0: identifier=243FA6B4-33AE-43A6-8567-AFE11518BBCC, label=_$!<Home>!$_, value=<CNPhoneNumber: 0x7f8f4fb88ca0: countryCode=us, digits=+8613088488288>>"
             ), emailAddresses=(
             "<CNLabeledValue: 0x7f8f4fb87c30: identifier=3B1CADF7-967B-41A0-A575-EB0B7BA1BB5B, label=_$!<Home>!$_, value=dylan@china.com>"
             ), postalAddresses=(not fetched)>
             */
    }];
        
    if ( b ) {
            ILog(@"Search success.");
    }
    

    当然不能所有的代码全部我贴出来,关于保存等功能,大家自行探索。

    2016-8-23 上午10:00 copyRight@dylan@china.com 欢迎转载。

    相关文章

      网友评论

        本文标题:AddressBook.framework引导

        本文链接:https://www.haomeiwen.com/subject/dknmvttx.html