操作系统通讯录实现添加与读取联系人


引言:这是我之前写在博客园的一篇记录笔记,因个人比较喜欢简书的书写环境与阅读气氛,所以统一整理到这里,做个备忘吧O(∩_∩)O哈哈~!


苹果提供了访问系统通讯录的框架,以便开发者对系统通讯录进行操作。想要访问通讯录,需要添加AddressBookUI.framework和AddressBook.framework两个框架,添加的地点这里就不在赘述了。在控制器内部首先import两个头文件,<AddressBook/AddressBook.h> 和 <AddressBookUI/AddressBookUI.h>,如下所示:

//  ZBSampleViewController.m
//  ZBAddressBookDemo
//
//  Created by zhangb on 16/02/04.
//  Copyright (c) 2016年 mbp. All rights reserved.
//

#import "ZBSampleViewController.h"
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>

首先为了方便演示与操作,这里就以两个按钮的操作代替具体的访问过程,当然具体操作要具体分析,这里只是记录访问通讯录,包括:

    1. 查看联系人
    1. 向通讯录内添加联系人。
      下面是代码示例:ABPeoplePickerNavigationController为展示系统通讯录的控制器,并且需要遵循其代理方法。
#define IS_iOS8 [[UIDevice currentDevice].systemVersion floatValue] >= 8.0f
#define IS_iOS6 [[UIDevice currentDevice].systemVersion floatValue] >= 6.0f

@interface ZBSampleViewController ()<ABPeoplePickerNavigationControllerDelegate>{

    ABPeoplePickerNavigationController *_abPeoplePickerVc;
    NSMutableDictionary                *_infoDictionary;
    
}
@end
- (void)viewDidLoad {
    [super viewDidLoad];
    
    //1.打开通讯录
    UIButton *openAddressBook = [UIButton buttonWithType:UIButtonTypeCustom];
    openAddressBook.frame = CGRectMake(100, 50, 100, 50);
    [openAddressBook setTitle:@"打开通讯录" forState:UIControlStateNormal];
    openAddressBook.backgroundColor = [UIColor greenColor];
    [openAddressBook setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [openAddressBook addTarget:self action:@selector(gotoAddressBook) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:openAddressBook];
    
    //2.添加联系人
    UIButton *addContacts = [UIButton buttonWithType:UIButtonTypeCustom];
    addContacts.frame = CGRectMake(100, 150, 100, 50);
    [addContacts setTitle:@"添加联系人" forState:UIControlStateNormal];
    addContacts.backgroundColor = [UIColor greenColor];
    [addContacts setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [addContacts addTarget:self action:@selector(gotoAddContacts) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:addContacts];

}

打开系统通讯录方法为openAddressBook按钮的点击事件,请忽略按钮的样式O(∩_∩)O~;
下面的IS_iOS8为我定义的宏,判断系统的版本(上面有代码示例)。

/**
 打开通讯录
 */
- (void)gotoAddressBook{
    
    _abPeoplePickerVc = [[ABPeoplePickerNavigationController alloc] init];
    _abPeoplePickerVc.peoplePickerDelegate = self;
    
    //下面的判断是ios8之后才需要加的,不然会自动返回app内部
    if(IS_iOS8){
        
        //predicateForSelectionOfPerson默认是true (当你点击某个联系人查看详情的时候会返回app),如果你默认为true 但是实现-peoplePickerNavigationController:didSelectPerson:property:identifier:代理方法也是可以的,与此同时不能实现peoplePickerNavigationController: didSelectPerson:不然还是会返回app。
        //总之在ios8之后加上此句比较稳妥
        _abPeoplePickerVc.predicateForSelectionOfPerson = [NSPredicate predicateWithValue:false];
        
//        predicateForSelectionOfProperty默认是true (当你点击某个联系人的某个属性的时候会返回app),此方法只要是默认值,无论你代理方法实现与否都会返回app。
        _abPeoplePickerVc.predicateForSelectionOfProperty = [NSPredicate predicateWithValue:false];
        
        //predicateForEnablingPerson默认是true,当设置为false时,所有的联系人都不能被点击。
//        _abPeoplePickerVc.predicateForEnablingPerson = [NSPredicate predicateWithValue:true];
    }
    [self presentViewController:_abPeoplePickerVc animated:YES completion:nil];
    
}

这里需要注意的是:
在iOS8之后需要加_abPeoplePickerVc.predicateForSelectionOfPerson = [NSPredicate predicateWithValue:false];这句代码,不然当你选择通讯录中的某个联系人的时候会直接返回app内部(类似crash)。predicateForSelectionOfPerson默认是true (当你点击某个联系人查看详情的时候会返回app),如果你默认为true 但是实现-peoplePickerNavigationController:didSelectPerson:property:identifier:代理方法也是可以的,与此同时不能实现peoplePickerNavigationController: didSelectPerson:不然还是会返回app。_abPeoplePickerVc.predicateForSelectionOfProperty = [NSPredicate predicateWithValue:false];
作用同上。但是_abPeoplePickerVc.predicateForEnablingPerson 的断言语句必须为true,否则任何联系人你都不能选择。上面的代码中也有详细描述。

#pragma mark - ABPeoplePickerNavigationController的代理方法

- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier {
    
    ABMultiValueRef phone = ABRecordCopyValue(person, kABPersonPhoneProperty);
    
    long index = ABMultiValueGetIndexForIdentifier(phone,identifier);
    
    NSString *phoneNO = (__bridge NSString *)ABMultiValueCopyValueAtIndex(phone, index);
    [phoneNO stringByReplacingOccurrencesOfString:@"-" withString:@""];
    if (phone && phoneNO.length == 11) {
        //TODO:获取电话号码要做的事情
         
        [peoplePicker dismissViewControllerAnimated:YES completion:nil];
        return;
    }else{
        if (IS_iOS8){
            UIAlertController *tipVc = [UIAlertController alertControllerWithTitle:nil message:@"请选择正确手机号" preferredStyle:UIAlertControllerStyleAlert];
            UIAlertAction *cancleAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
                [self dismissViewControllerAnimated:YES completion:nil];
            }];
            [tipVc addAction:cancleAction];
            [self presentViewController:tipVc animated:YES completion:nil];
            
        }else{
            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:@"请选择正确手机号" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil];
            [alertView show];
        }
        //非ARC模式需要释放对象
//        [alertView release];
    }
}

- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person NS_AVAILABLE_IOS(8_0)
{
    ABPersonViewController *personViewController = [[ABPersonViewController alloc] init];
    personViewController.displayedPerson = person;
    
    [peoplePicker pushViewController:personViewController animated:YES];
    //非ARC模式需要释放对象
//    [personViewController release];
}

/**
 peoplePickerNavigationController点击取消按钮时调用
 */
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker
{
    [peoplePicker dismissViewControllerAnimated:YES completion:nil];
}

/**
 iOS8被废弃了,iOS8前查看联系人必须实现(点击联系人可以继续操作)
 */
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person NS_DEPRECATED_IOS(2_0, 8_0)
{
    return YES;
}

/**
 iOS8被废弃了,iOS8前查看联系人属性必须实现(点击联系人属性可以继续操作)
 */
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier NS_DEPRECATED_IOS(2_0, 8_0)
{
    ABMultiValueRef phone = ABRecordCopyValue(person, kABPersonPhoneProperty);
    
    long index = ABMultiValueGetIndexForIdentifier(phone,identifier);
    
    NSString *phoneNO = (__bridge NSString *)ABMultiValueCopyValueAtIndex(phone, index);
    phoneNO = [phoneNO stringByReplacingOccurrencesOfString:@"-" withString:@""];
    NSLog(@"%@", phoneNO);
    if (phone && phoneNO.length == 11) {
        //TODO:获取电话号码要做的事情
        
        [peoplePicker dismissViewControllerAnimated:YES completion:nil];
        return NO;
    }else{
        if (IS_iOS8){
            UIAlertController *tipVc = [UIAlertController alertControllerWithTitle:nil message:@"请选择正确手机号" preferredStyle:UIAlertControllerStyleAlert];
            UIAlertAction *cancleAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
                [self dismissViewControllerAnimated:YES completion:nil];
            }];
            [tipVc addAction:cancleAction];
            [self presentViewController:tipVc animated:YES completion:nil];
            
        }else{
            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:@"请选择正确手机号" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil];
            [alertView show];
        }
    }
    return YES;
}

ABPeoplePickerNavigationController的代理方法也很好理解,看字面意思就能够猜出代理方法的执行时间与能够做什么。拿第一个代理方法说明一下,也就是- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier;第一个参数就不用说了,第二个参数ABRecordRef 这是一个联系人的引用也可以说是一个记录,你可以理解为一个联系人。第三个参数是联系人附带属性的ID,其实ABPropertyID是个int类型值。第四个参数是多值属性的标签。

ABRecordCopyValue(ABRecordRef record, ABPropertyID property)返回一个CFTypeRef类型。此方法是从系统的通讯录内,copy出用户所选择的某个联系人数据。返回类型要看参数传递的是什么,第一个参数是联系人记录,第二个参数是参数ID,也就是用户选择的联系人的某个属性的ID。kABPersonPhoneProperty代表的是手机号码属性的ID,假如上面的ABRecordCopyValue的第二个参数传递kABPersonAddressProperty,则返回的是联系人的地址属性。下面是效果图(因为是模拟器和真机有些差异)。

读取联系人.gif

下面介绍下,向系统通讯录内添加联系人。这里我只设置了联系人的三个属性:名字,电话,邮件,并将属性存在了字典里。

-(instancetype)init{
    if (self = [super init]) {
        _infoDictionary = [NSMutableDictionary dictionaryWithCapacity:0];
        [_infoDictionary setObject:@"张三" forKey:@"name"];
        [_infoDictionary setObject:@"13000000000" forKey:@"phone"];
        [_infoDictionary setObject:@"1000000000@qq.com" forKey:@"email"];
    }
    return self;
}

因为苹果越来越注重保护用户的隐私,现在需要修改系统通讯录内的联系人信息时,必须要用户授权才可以进行。授权鉴定代码为:

ABAddressBookRequestAccessWith
Completion
(ABAddressBookRef addressBook,
ABAddressBookRequestAccessCompletionHandler completion)__OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_6_0);

下面的代码中都有详细描述。

/**
 添加联系人
 */
- (void)gotoAddContacts{
    
    //添加到通讯录,判断通讯录是否存在
    if ([self isExistContactPerson]) {//存在,返回
        //提示
        if (IS_iOS8) {
            UIAlertController *tipVc  = [UIAlertController alertControllerWithTitle:@"提示" message:@"联系人已存在..." preferredStyle:UIAlertControllerStyleAlert];
            UIAlertAction *cancleAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
                [self dismissViewControllerAnimated:YES completion:nil];
            }];
            [tipVc addAction:cancleAction];
            [self presentViewController:tipVc animated:YES completion:nil];
        }else{
            UIAlertView *tip = [[UIAlertView alloc] initWithTitle:@"提示" message:@"联系人已存在..." delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
            [tip show];
            //        [tip release];
        }
        return;
    }else{//不存在  添加
        [self creatNewRecord];
    }
}

- (BOOL)isExistContactPerson{
    //这个变量用于记录授权是否成功,即用户是否允许我们访问通讯录
    int __block tip=0;
    
    BOOL __block isExist = NO;
    //声明一个通讯簿的引用
    ABAddressBookRef addBook =nil;
    //因为在IOS6.0之后和之前的权限申请方式有所差别,这里做个判断
    if (IS_iOS6) {
        //创建通讯簿的引用,第一个参数暂时写NULL,官方文档就是这么说的,后续会有用,第二个参数是error参数
        CFErrorRef error = NULL;
        addBook=ABAddressBookCreateWithOptions(NULL, &error);
        //创建一个初始信号量为0的信号
        dispatch_semaphore_t sema=dispatch_semaphore_create(0);
        //申请访问权限
        ABAddressBookRequestAccessWithCompletion(addBook, ^(bool greanted, CFErrorRef error)        {
            //greanted为YES是表示用户允许,否则为不允许
            if (!greanted) {
                tip=1;
                
            }else{
                //获取所有联系人的数组
                CFArrayRef allLinkPeople = ABAddressBookCopyArrayOfAllPeople(addBook);
                //获取联系人总数
                CFIndex number = ABAddressBookGetPersonCount(addBook);
                //进行遍历
                for (NSInteger i=0; i<number; i++) {
                    //获取联系人对象的引用
                    ABRecordRef  people = CFArrayGetValueAtIndex(allLinkPeople, i);
                    //获取当前联系人名字
                    NSString*firstName=(__bridge NSString *)(ABRecordCopyValue(people, kABPersonFirstNameProperty));
                    
                    if ([firstName isEqualToString:[_infoDictionary objectForKey:@"name"]]) {
                        isExist = YES;
                    }
                    
//                    //获取当前联系人姓氏
//                    NSString*lastName=(__bridge NSString *)(ABRecordCopyValue(people, kABPersonLastNameProperty));

//获取当前联系人中间名
//                    NSString*middleName=(__bridge NSString*)(ABRecordCopyValue(people, kABPersonMiddleNameProperty));
//                    //获取当前联系人的名字前缀
//                    NSString*prefix=(__bridge NSString*)(ABRecordCopyValue(people, kABPersonPrefixProperty));
//                    //获取当前联系人的名字后缀
//                    NSString*suffix=(__bridge NSString*)(ABRecordCopyValue(people, kABPersonSuffixProperty));
//                    //获取当前联系人的昵称
//                    NSString*nickName=(__bridge NSString*)(ABRecordCopyValue(people, kABPersonNicknameProperty));
//                    //获取当前联系人的名字拼音
//                    NSString*firstNamePhoneic=(__bridge NSString*)(ABRecordCopyValue(people, kABPersonFirstNamePhoneticProperty));
//                    //获取当前联系人的姓氏拼音
//                    NSString*lastNamePhoneic=(__bridge NSString*)(ABRecordCopyValue(people, kABPersonLastNamePhoneticProperty));
//                    //获取当前联系人的中间名拼音
//                    NSString*middleNamePhoneic=(__bridge NSString*)(ABRecordCopyValue(people, kABPersonMiddleNamePhoneticProperty));
//                    //获取当前联系人的公司
//                    NSString*organization=(__bridge NSString*)(ABRecordCopyValue(people, kABPersonOrganizationProperty));
//                    //获取当前联系人的职位
//                    NSString*job=(__bridge NSString*)(ABRecordCopyValue(people, kABPersonJobTitleProperty));
//                    //获取当前联系人的部门
//                    NSString*department=(__bridge NSString*)(ABRecordCopyValue(people, kABPersonDepartmentProperty));
//                    //获取当前联系人的生日
//                    NSString*birthday=(__bridge NSDate*)(ABRecordCopyValue(people, kABPersonBirthdayProperty));
//                    NSMutableArray * emailArr = [[NSMutableArray alloc]init];
//                    //获取当前联系人的邮箱 注意是数组
//                    ABMultiValueRef emails= ABRecordCopyValue(people, kABPersonEmailProperty);
//                    for (NSInteger j=0; j<ABMultiValueGetCount(emails); j++) {
//                        [emailArr addObject:(__bridge NSString *)(ABMultiValueCopyValueAtIndex(emails, j))];
//                    }
//                    //获取当前联系人的备注
//                    NSString*notes=(__bridge NSString*)(ABRecordCopyValue(people, kABPersonNoteProperty));
//                    //获取当前联系人的电话 数组
//                    NSMutableArray * phoneArr = [[NSMutableArray alloc]init];
//                    ABMultiValueRef phones= ABRecordCopyValue(people, kABPersonPhoneProperty);
//                    for (NSInteger j=0; j<ABMultiValueGetCount(phones); j++) {
//                        [phoneArr addObject:(__bridge NSString *)(ABMultiValueCopyValueAtIndex(phones, j))];
//                    }
//                    //获取创建当前联系人的时间 注意是NSDate
//                    NSDate*creatTime=(__bridge NSDate*)(ABRecordCopyValue(people, kABPersonCreationDateProperty));
//                    //获取最近修改当前联系人的时间
//                    NSDate*alterTime=(__bridge NSDate*)(ABRecordCopyValue(people, kABPersonModificationDateProperty));
//                    //获取地址
//                    ABMultiValueRef address = ABRecordCopyValue(people, kABPersonAddressProperty);
//                    for (int j=0; j<ABMultiValueGetCount(address); j++) {
//                        //地址类型
//                        NSString * type = (__bridge NSString *)(ABMultiValueCopyLabelAtIndex(address, j));
//                        NSDictionary * temDic = (__bridge NSDictionary *)(ABMultiValueCopyValueAtIndex(address, j));
//                        //地址字符串,可以按需求格式化
//                        NSString * adress = [NSString stringWithFormat:@"国家:%@\n省:%@\n市:%@\n街道:%@\n邮编:%@",[temDic valueForKey:(NSString*)kABPersonAddressCountryKey],[temDic valueForKey:(NSString*)kABPersonAddressStateKey],[temDic valueForKey:(NSString*)kABPersonAddressCityKey],[temDic valueForKey:(NSString*)kABPersonAddressStreetKey],[temDic valueForKey:(NSString*)kABPersonAddressZIPKey]];
//                    }
//                    //获取当前联系人头像图片
//                    NSData*userImage=(__bridge NSData*)(ABPersonCopyImageData(people));
//                    //获取当前联系人纪念日
//                    NSMutableArray * dateArr = [[NSMutableArray alloc]init];
//                    ABMultiValueRef dates= ABRecordCopyValue(people, kABPersonDateProperty);
//                    for (NSInteger j=0; j<ABMultiValueGetCount(dates); j++) {
//                        //获取纪念日日期
//                        NSDate * data =(__bridge NSDate*)(ABMultiValueCopyValueAtIndex(dates, j));
//                        //获取纪念日名称
//                        NSString * str =(__bridge NSString*)(ABMultiValueCopyLabelAtIndex(dates, j));
//                        NSDictionary * temDic = [NSDictionary dictionaryWithObject:data forKey:str];
//                        [dateArr addObject:temDic];
//                    }
                }
                
                
            }
            //发送一次信号
            dispatch_semaphore_signal(sema);
        });
        //等待信号触发
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    }else{
        
        //IOS6之前
        addBook =ABAddressBookCreate();
        
        //获取所有联系人的数组
        CFArrayRef allLinkPeople = ABAddressBookCopyArrayOfAllPeople(addBook);
        //获取联系人总数
        CFIndex number = ABAddressBookGetPersonCount(addBook);
        //进行遍历
        for (NSInteger i=0; i<number; i++) {
            //获取联系人对象的引用
            ABRecordRef  people = CFArrayGetValueAtIndex(allLinkPeople, i);
            //获取当前联系人名字
            NSString*firstName=(__bridge NSString *)(ABRecordCopyValue(people, kABPersonFirstNameProperty));
            
            if ([firstName isEqualToString:[_infoDictionary objectForKey:@"name"]]) {
                isExist = YES;
            }
        }
    }
    
    if (tip) {
        //设置提示
        if (IS_iOS8) {
            UIAlertController *tipVc  = [UIAlertController alertControllerWithTitle:@"友情提示" message:@"请您设置允许APP访问您的通讯录\nSettings>General>Privacy" preferredStyle:UIAlertControllerStyleAlert];
            UIAlertAction *cancleAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
                [self dismissViewControllerAnimated:YES completion:nil];
            }];
            [tipVc addAction:cancleAction];
            [tipVc presentViewController:tipVc animated:YES completion:nil];
        }else{
            UIAlertView * alart = [[UIAlertView alloc]initWithTitle:@"友情提示" message:@"请您设置允许APP访问您的通讯录\nSettings>General>Privacy" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil, nil];
            [alart show];
            //非ARC
//            [alart release];
        }
    }
    return isExist;
}

//创建新的联系人
- (void)creatNewRecord
{
    CFErrorRef error = NULL;
    
    //创建一个通讯录操作对象
    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
    
    //创建一条新的联系人纪录
    ABRecordRef newRecord = ABPersonCreate();
    
    //为新联系人记录添加属性值
    ABRecordSetValue(newRecord, kABPersonFirstNameProperty, (__bridge CFTypeRef)[_infoDictionary objectForKey:@"name"], &error);
    
    //创建一个多值属性(电话)
    ABMutableMultiValueRef multi = ABMultiValueCreateMutable(kABMultiStringPropertyType);
    ABMultiValueAddValueAndLabel(multi, (__bridge CFTypeRef)[_infoDictionary objectForKey:@"phone"], kABPersonPhoneMobileLabel, NULL);
    ABRecordSetValue(newRecord, kABPersonPhoneProperty, multi, &error);
    
    //添加email
    ABMutableMultiValueRef multiEmail = ABMultiValueCreateMutable(kABMultiStringPropertyType);
    ABMultiValueAddValueAndLabel(multiEmail, (__bridge CFTypeRef)([_infoDictionary objectForKey:@"email"]), kABWorkLabel, NULL);
    ABRecordSetValue(newRecord, kABPersonEmailProperty, multiEmail, &error);
    
    
    //添加记录到通讯录操作对象
    ABAddressBookAddRecord(addressBook, newRecord, &error);
    
    //保存通讯录操作对象
    ABAddressBookSave(addressBook, &error);
    
    //通过此接口访问系统通讯录
    ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
        
        if (granted) {
            //显示提示
            if (IS_iOS8) {
                UIAlertController *alertVc = [UIAlertController alertControllerWithTitle:@"添加成功" message:nil preferredStyle:UIAlertControllerStyleAlert];
                UIAlertAction *alertAction = [UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
                    [self dismissViewControllerAnimated:YES completion:nil];
                    
                }];
                [alertVc addAction:alertAction];
                [self presentViewController:alertVc animated:YES completion:nil];
            }else{
            
                UIAlertView *tipView = [[UIAlertView alloc] initWithTitle:nil message:@"添加成功" delegate:self cancelButtonTitle:@"知道了" otherButtonTitles:nil, nil];
                [tipView show];
                //非ARC
//                [tipView release];
            }
        }
    });
    
    CFRelease(multiEmail);
    CFRelease(multi);
    CFRelease(newRecord);
    CFRelease(addressBook);
}

代码中有每个步骤都有对应的注释,对应看起来会容易一些!下面是效果图:

添加联系人.gif

如果需要添加联系人的其他属性,方法类似,只是属性名不同。慢慢挖掘吧,伙伴们!

声明:此文仅为了记录本人开发中的遇到并解决的问题,权当是一篇笔记,并不是教学blog,不免会有错误,如有烦请指正,大家共同学习!谢谢!

下面列举一些参考blog:(再次感谢无私分享的coder)
http://m.open-open.com/m/code/view/1432302834146
http://supershll.blog.163.com/blog/static/37070436201272821810474/
http://www.tuicool.com/articles/Mvuu6z

注意:iOS10以及Xcode8的缘故,需要适配,需要添加访问权限设置,也就是在info.plist中添加key:Privacy - Contacts Usage Description value:对应的value值可以随便写,不然程序会崩溃!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,507评论 6 13
  • 为什么要上传到JCenter 为了一句话依赖 注册bintray账号 既然是通过bintray上传,那得先注册bi...
    X_Sation阅读 757评论 4 2
  • (索科洛夫/文) 我多么希望,这几行诗 忘记它们自己是一些字 而成为湿润的林荫道上的 树木、天空、清风和房子 但愿...
    辛艳平阅读 4,110评论 0 1