iOS Address Book指南

尽管OC是同一山头面向对象的语言,但是于您开开发之上你见面发现,并无是具有你用之frameworks都是面向对象的。有些是为此C写的,例如Address
Book的API,接下去吃咱们错过学习一下Address Book。

咱俩在咱们的APP中得以Address Book
API来读取或者修改用户联系人的信(这跟我们在大哥大通讯录上的机能是同等的)。

因Address Book
API是根据C语言的,它不是动的对象,而且她吗应用了片别样的品种,在此,你用会熟悉一下几单API:

  • ABRecordRef:它是一个联系记录,包括了装有的属性,例如手机,电话,电子邮件,姓,名等等。
  • ABAddresBookRef:它是享有用户联系人的联谊,你得本着记录进行追加、修改和去。
  • ABMutableMultiValueRef:它是ABMultiValueRef的可变类型(类似于NSDictionary的NSMutableDictionary),虽然她是便于之,但是它们要求您设置ABRecordeRef属性的时发生差不多个实体,例如电话号码或者email.

既是读这文章,那么就是意味着你针对iOS开发出一个基础之询问,而且熟悉C的根底语法。如果你莫满足才说的有数只尺码,可以先对iOS进行复习或者先了解C语言。

吓了,让咱们开始学吧!

开始

先是,你可以先到此地下载此界面程序,然后于是基础及进行付出上Address
Book。(这个序非常简单,就是拓宽了4独button,然后来了一个出口,用不同的tag标记不同按钮)

动Address Book
API,你待导入头文件,导入方式如下:

@import AddressBook;

抑或直接:

#import <AddressBook/AddressBook.h> 

于此小Demo中,用户将得以点击任何一个图片,然后是宠物联系人之音讯就是会储存到address
Book里面。使用Address Book API,你可联系到您的囤的情人。

恳请权限

以2012年,有一个争论:app是否可复制用户的通讯录,然后用数据发送至祥和之服务器。大众的应肯定是未允,就到底发送也如经过用户同意。所有Apple就生了一个初的风味:请求权限。防止用户在不知情的情形下自己之报导录被APP盗取。

从而,现在而您想使用Address Book,你首先要抱用户之许。

吃咱们来尝试做一下,在ViewController.m中,添加如下代码到按钮点击事件:

- (IBAction)tapAction:(id)sender {
    //iOS 8 and before
    if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusDenied ||ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusRestricted) {
//1
        NSLog(@"Denied");
    }else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
//2        
NSLog(@"Authorized");
    }else {
//3
        NSLog(@"Not determined");
    }

//    //iOS 9 and later
//    CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
//    if (status == CNAuthorizationStatusDenied || status == CNAuthorizationStatusRestricted)  {
//        NSLog(@"Denied");
//    }else if (status == CNAuthorizationStatusAuthorized ) {
//        NSLog(@"Authorized");
//    }else {
//        NSLog(@"Not determined");
//    }
}

为我们来分析一下:

  1. 这检查是故来检测用户是否不肯了而的app访问手机通讯录,或者是它们是被限制的(比如家长决定).如果用户拒绝了或限制了,那么您不得不报用户没有权限对通讯录进行操作,其他的什么吗无能为力做。
  2. 本条检查是望用户是否都同意你的APP访问用户的通讯录,如果同意了,你得随心所欲地修改要对通讯录进行其它操作。
  3. 斯检查是看用户是否还并未规定你的APP具有访问通讯录权限。

输出结果如下:

2016-09-13 16:46:35.513 ABContractDemo[14369:395357] Not determined

以及现实生活一样:你要什么东西的下,你待了解。

从而,你得请用户得到访问权限,在3之地方写如下代码:(这里就是未以介绍iOS9)

        ABAddressBookRequestAccessWithCompletion(ABAddressBookCreateWithOptions(NULL, nil), ^(bool granted, CFErrorRef error) {
            if (granted) {
                NSLog(@"Just authorized");
            }else {
                NSLog(@"Just deieny");
            }
        });

当时之中第一单参数是ABAddressBookRef,你用ABAddressBookCreateWithOptions(NULL,nil)。第二单参数是一个block:一旦用户点击了授权按钮,便会调用内的东西。

这次运行的结果虽是:

当您点击了Don’t
Allow,就标明iDenied了,如果ok就象征你允许了APP访问通讯录。

创立记录

现,让咱开失去创造通讯录记录。我们本清空按钮点击事件,然后重新写她。在再次写的此方法中,你待创造一个ABRecordRef,他概括了宠物的特性,检查一下通讯录确保无设有你长的牵连人,如果宠物不以通讯录,就拿他加盟到通讯录。

以tapAction:(id)sender方法里面写入:

  NSString *petFirstName;
    NSString *petLastName;
    NSString *petphoneNumber;
    NSData *petImageData;
    if (sender.tag == 1) {
        petFirstName = @"Cheesy";
        petLastName = @"Cat";
        petphoneNumber = @"201345345345";
        petImageData = UIImageJPEGRepresentation([UIImage imageNamed:@"contact_Cheesy.jpg"], 0.7f);
    }else if (sender.tag == 2) {
        petFirstName = @"Freckles";
        petLastName = @"Dog";
        petphoneNumber = @"2015434345345";
        petImageData = UIImageJPEGRepresentation([UIImage imageNamed:@"contact_Freckles.jpg"], 0.7f);
    }else if (sender.tag == 3) {
        petFirstName = @"Maxi";
        petLastName = @"Dog";
        petphoneNumber = @"1243504354";
        petImageData = UIImageJPEGRepresentation([UIImage imageNamed:@"contact_Maxi.jpg"], 0.7f);
    }else if(sender.tag == 4) {
        petFirstName = @"Shippo";
        petLastName = @"Dog";
        petphoneNumber = @"5406957657";
        petImageData = UIImageJPEGRepresentation([UIImage imageNamed:@"contact_Shippo.jpg"], 0.7f);
    }

经点击不同之按钮,可以规定点击的是孰宠物。接下来,写如下代码

    ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, nil);
    ABRecordRef pet = ABPersonCreate();

先是推行是创建一个ABAddressBookRef,它稍后用户用pet加至用户的报道录中。第二执是也汝的创立了一个缺损的记录,用来填充宠物的信息。

对接下,设置宠物的姓氏和名,代码如下:

 ABRecordSetValue(pet, kABPersonFirstNameProperty, (__bridge CFStringRef)(petFirstName), nil);
 ABRecordSetValue(pet, kABPersonLastNameProperty, (__bridge CFStringRef)(petLastName), nil);

简简单单的介绍:

  • ABRecordSetValue()把ABREcordRef作为第一单参数,它的记录是pet
  • 次只参数是ABPropertyID,这个是API定义的,因为若想装姓,所以传入kABPersonFirstNameProperty
  • 对于称,类似地传来kABPersonLastNameProperty

老三独参数看起困惑吗?它是一个CFTypeRef,该项目包括了CFStringRef和ABMultiValueRef,你要传递CFStringRef,但是若下NSString。为了以NSString
转换成CFTypeRef,使用(__bridge CFStringRef) myString。

手机号的粗复杂一点,因为一个联系人可以生多个手机号(家庭,手机,等等),因此此要采取ABMutableMultiValueRef。这些可通过下的代码完成,(在上头代码后面继续累加):

    ABMutableMultiValueRef phoneNumbers = ABMultiValueCreateMutable(kABMultiStringPropertyType);
    ABMultiValueAddValueAndLabel(phoneNumbers, (__bridge CFTypeRef)(petphoneNumber), kABPersonPhoneMainLabel, NULL);

当你声明ABMutableMultiValueRef,你得证明是呀性质。在即时之中,你想它是kABPersonPhoneProperty。第二执是补充加pet’s
Phone number,这里注意你要被这号码一个label.这个label
kABPersonPhoneMainLabel 说明这号码是用户太要的号子。然后是加上相片:

ABPersonSetImageData(pet, (__bridge CFDataRef)petImageData, nil);

说到底是拿关系人的音信保存至通讯录中:

ABAddressBookAddRecord(addressBookRef, pet, nil);
ABAddressBookSave(addressBookRef, nil);

接通下运行,然后点击每个按钮,就可以内容存储到温馨本机的通讯录中了。

可你会意识一个题材,如果一致点击某个按钮,那么这个宠物的信就会直接往通讯录内长。为了避免复制,你应该循环访问具有的通讯录信息管新的通讯录记录名字不在通讯录内。

插以下代码到ABAddressBookAddRecord() 。首先,添加这无异履行:

NSArray *allContracts = (__bridge NSArray *)(ABAddressBookCopyArrayOfAllPeople(addressBookRef));

这里可以小心到:你可以__bridge将对象在Core
Foundation对象转换成为Foundation,也得以拿Foundation转成为Core Foundation。

接下来,添加以下代码:

    for (id record in allContracts) {
        ABRecordRef thisContract = (__bridge ABRecordRef)(record);
        if (CFStringCompare(ABRecordCopyCompositeName(thisContract), ABRecordCopyCompositeName(pet), 0) == kCFCompareEqualTo) {
            //用户已经存在
            NSLog(@"用户已经存在");
            break;

        }
    }

卿必须使用id,因为从技术上来讲,Core
Foundation类型是无克让换成为NSArray的,因为她俩非是目标。ABRecordRefs被伪装成id来避免失误。所以为了取得ABRecordRef,还索要采取重复用__bridge。

以CFStringCompare的不二法门接近于NSString的isEqualToString。ABRecordCopyCompositeName得到了人名,它是维系人姓和名之三结合。这样尽管足以据此来防护重复记录了。

多线程

得了到这里上面的整体代码如下:

- (IBAction)tapAction:(UIButton *)sender {
    if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusDenied ||ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusRestricted) {
        //1
        NSLog(@"Denied");
    }else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
        //2
        NSLog(@"Authorized");
    }else {
        //3
        ABAddressBookRequestAccessWithCompletion(ABAddressBookCreateWithOptions(NULL, nil), ^(bool granted, CFErrorRef error) {
            if (granted) {
                NSLog(@"Just authorized");
            }else {
                NSLog(@"Just deieny");
            }
        });
    }





    NSString *petFirstName;
    NSString *petLastName;
    NSString *petphoneNumber;
    NSData *petImageData;
    if (sender.tag == 1) {
        petFirstName = @"Cheesy";
        petLastName = @"Cat";
        petphoneNumber = @"201345345345";
        petImageData = UIImageJPEGRepresentation([UIImage imageNamed:@"contact_Cheesy.jpg"], 0.7f);
    }else if (sender.tag == 2) {
        petFirstName = @"Freckles";
        petLastName = @"Dog";
        petphoneNumber = @"2015434345345";
        petImageData = UIImageJPEGRepresentation([UIImage imageNamed:@"contact_Freckles.jpg"], 0.7f);
    }else if (sender.tag == 3) {
        petFirstName = @"Maxi";
        petLastName = @"Dog";
        petphoneNumber = @"1243504354";
        petImageData = UIImageJPEGRepresentation([UIImage imageNamed:@"contact_Maxi.jpg"], 0.7f);
    }else if(sender.tag == 4) {
        petFirstName = @"Shippo";
        petLastName = @"Dog";
        petphoneNumber = @"5406957657";
        petImageData = UIImageJPEGRepresentation([UIImage imageNamed:@"contact_Shippo.jpg"], 0.7f);
    }
    ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, nil);   //通讯录
    ABRecordRef pet = ABPersonCreate();   //一条记录

    //设置姓名
    ABRecordSetValue(pet, kABPersonFirstNameProperty, (__bridge CFStringRef)(petFirstName), nil);
    ABRecordSetValue(pet, kABPersonLastNameProperty, (__bridge CFStringRef)(petLastName), nil);

    //设置手机号
    ABMutableMultiValueRef phoneNumbers = ABMultiValueCreateMutable(kABMultiStringPropertyType);
    ABMultiValueAddValueAndLabel(phoneNumbers, (__bridge CFTypeRef)(petphoneNumber), kABPersonPhoneMainLabel, NULL);
    ABRecordSetValue(pet, kABPersonPhoneProperty, phoneNumbers, nil);

    //设置照片
    CFErrorRef *error;
    ABPersonSetImageData(pet, (__bridge CFDataRef)petImageData, error);

    //获取所有联系人
    NSArray *allContracts = (__bridge NSArray *)(ABAddressBookCopyArrayOfAllPeople(addressBookRef));
    for (id record in allContracts) {
        ABRecordRef thisContract = (__bridge ABRecordRef)(record);
        if (CFStringCompare(ABRecordCopyCompositeName(thisContract), ABRecordCopyCompositeName(pet), 0) == kCFCompareEqualTo) {
            //用户已经存在
            NSLog(@"用户已经存在");
            break;

        }
    }

    ABAddressBookAddRecord(addressBookRef, pet, nil);
    ABAddressBookSave(addressBookRef, nil);









    //    //iOS 8 and before
//    if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusDenied ||ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusRestricted) {
//        NSLog(@"Denied");
//    }else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
//        NSLog(@"Authorized");
//    }else {
//        ABAddressBookRequestAccessWithCompletion(ABAddressBookCreateWithOptions(NULL, nil), ^(bool granted, CFErrorRef error) {
//            if (granted) {
//                NSLog(@"Just authorized");
//            }else {
//                NSLog(@"Just deieny");
//            }
//        });
//    }
//    
////    //iOS 9 and later
////    CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
////    if (status == CNAuthorizationStatusDenied || status == CNAuthorizationStatusRestricted)  {
////        NSLog(@"Denied");
////    }else if (status == CNAuthorizationStatusAuthorized ) {
////        NSLog(@"Authorized");
////    }else {
////        NSLog(@"Not determined");
////    }
}

View Code

此地尚闹只藏匿的问题,如果你看了ABAddressBookRequestAccessWithCompletion的官文档,刚才的点击事件是在随意的行上调用的。换句话说,也就算是她实施可能当旁的线程上,不自然当主线程。

当下中你不能不使明:用户图形界面展示只能在主线程上。你必管其他影响用户图像化界面显示的代码都要于主线程上调用。

动用下的代码可以生轻之完成。在ABAddressBookRequestWithCompletion之前运用:

    dispatch_async(dispatch_get_main_queue(), ^{
        <#code#>
    });

是是于主线程上执行,可以用用户图形化展示。如果想学学又多,可以翻阅这里。

下一场利用上述block块进行如下操作:

ABAddressBookRequestAccessWithCompletion(ABAddressBookCreateWithOptions(NULL, nil), ^(bool granted, CFErrorRef error) {
  dispatch_async(dispatch_get_main_queue(), ^{
    if (!granted){ 
      //4
      UIAlertView *cantAddContactAlert = [[UIAlertView alloc] initWithTitle: @"Cannot Add Contact" message: @"You must give the app permission to add the contact first." delegate:nil cancelButtonTitle: @"OK" otherButtonTitles: nil];
      [cantAddContactAlert show];
      return;        
    }
    //5
    //添加通讯录操作
  });
});

及时是最好之法子去请用户得到通讯录权限,最好之实行就是以你真正使用的早晚才去告权限。如果你在开行之下就告用户权限,用户就是见面猜疑,因为用户不知道你怎么要因此到通讯录。

再有一个题材不怕是有关ABAddressBookRequestAccessWithCompletion,如果用户被了APP权限,有的时候用来5-10s的推,直到回调被调用。这看起好像当我们以添加通讯录记录之饥饿时程序是轧底状态。在多数状况下,这种题材并无常见。