iOS9联系人保存详解

       苹果在iOS9的SDK中废除了AddressBookUI.framework的一些功能(是不是这个库都废除了,有待验证),具体和保存联系人相关的几个系统界面如下:
联系人选择:AddressBookUI/ABPeoplePickerNavigationController.h
联系人详情:AddressBookUI/ABPersonViewController.h
未知联系人:AddressBookUI/ABUnknownPersonViewController.h
新建联系人:AddressBookUI/ABNewPersonViewController.h
新的SDK中使用
联系人选择:ContactsUI/CNContactPickerViewController.h
联系人详情、新建联系人、未知联系人:ContactsUI/CNContactViewController.h(使用不同方法创建,下面会说)
下面的链接是官方的ppt,讲的很详细,不过是swift版本
http://devstreaming.apple.com/videos/wwdc/2015/223rmo6dv9hxh/223/223_introducing_the_contacts_framework_for_ios_and_os_x.pdf
我要分享的是OC的实现,下面是我的实例。

一.需求
我的老项目里面的需求和微信的效果是一样的(下图为微信截图),这里先吐槽下,这个功能实现起来要区分ios7(6),ios8,ios9

 
二.思路
保存新联系人非常简单使用系统的新建联系人页,把需要保存的对象封装传入,剩下的交给系统处理就好;
保存现有联系人可能就费点心思了,有两种方式一个是使用未知联系人页面处理(我的客户比较NB,必须按需求100%实现),另一种是先跳转到选择页面中获取一个现有联系人的对象,再跳转到新建好友页面(把该对象传入),之后的事情交给系统处理(这个困扰了我很久,后来受到微信的启发,才想出来这个野路子)

三.实现(模拟器无法保存,请用真机调试),demo:  Contact4iOS9.zip (38
K) 下载次数:36 
导入头文件
#import <ContactsUI/CNContactViewController.h>
#import <ContactsUI/CNContactPickerViewController.h>

保存新联系人实现
- (void)saveNewContact{
    //1.创建Contact对象,必须是可变的
    CNMutableContact *contact = [[CNMutableContact alloc] init];
    //2.为contact赋值,这块比较恶心,很混乱,setValue4Contact中会给出常用值的对应关系
    [self setValue4Contact:contact existContect:NO];
    //3.创建新建好友页面
    CNContactViewController *controller = [CNContactViewController viewControllerForNewContact:contact];
    //代理内容根据自己需要实现
    controller.delegate = self;
    //4.跳转
    UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController:controller];
    [self presentViewController:navigation animated:YES completion:^{
        
    }];
    
}

保存现有联系人实现
//保存现有联系人实现
- (void)saveExistContact{
    //1.跳转到联系人选择页面,注意这里没有使用UINavigationController
    CNContactPickerViewController *controller = [[CNContactPickerViewController alloc] init];
    controller.delegate = self;
    [self presentViewController:controller animated:YES completion:^{
        
    }];
}

//2.实现点选的代理,其他代理方法根据自己需求实现
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact{
    [picker dismissViewControllerAnimated:YES completion:^{
        //3.copy一份可写的Contact对象,不要尝试alloc一类,mutableCopy独此一家
        CNMutableContact *c = [contact mutableCopy];
        //4.为contact赋值
        [self setValue4Contact:c existContect:YES];
        //5.跳转到新建联系人页面
        CNContactViewController *controller = [CNContactViewController viewControllerForNewContact:c];
        controller.delegate = self;
        UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController:controller];
        [self presentViewController:navigation animated:YES completion:^{
        }];
    }];
}

对应关系
//设置要保存的contact对象
- (void)setValue4Contact:(CNMutableContact *)contact existContect:(BOOL)exist{
    if (!exist) {
        //名字和头像
        contact.nickname = @"oriccheng";
        //        UIImage *logo = [UIImage imageNamed:@"..."];
        //        NSData *dataRef = UIImagePNGRepresentation(logo);
        //        contact.imageData = dataRef;
    }
    //电话,每一个CNLabeledValue都是有讲究的,如何批评,可以在头文件里面查找,这里给出几个常用的,别的我也不愿意去研究
    CNLabeledValue *phoneNumber = [CNLabeledValue labeledValueWithLabel:CNLabelPhoneNumberMobile value:[CNPhoneNumber
phoneNumberWithStringValue:@"18888888888"]];
    if (!exist) {
        contact.phoneNumbers = @[phoneNumber];
    }
    //现有联系人情况
    else{
        if ([contact.phoneNumbers count] >0) {
            NSMutableArray *phoneNumbers = [[NSMutableArray alloc] initWithArray:contact.phoneNumbers];
            [phoneNumbers addObject:phoneNumber];
            contact.phoneNumbers = phoneNumbers;
        }else{
            contact.phoneNumbers = @[phoneNumber];
        }
    }
    
    //网址:CNLabeledValue *url = [CNLabeledValue labeledValueWithLabel:@"" value:@""];
    //邮箱:CNLabeledValue *mail = [CNLabeledValue labeledValueWithLabel:CNLabelWork value:self.poiData4Save.mail];
    
    //特别说一个地址,PostalAddress对应的才是地址
    CNMutablePostalAddress *address = [[CNMutablePostalAddress alloc] init];
    address.state = @"辽宁省";
    address.city = @"沈阳市";
    address.postalCode = @"111111";
    //外国人好像都不强调区的概念,所以和具体地址拼到一起
    address.street = @"沈河区惠工街10号";
    //生成的上面地址的CNLabeledValue,其中可以设置类型CNLabelWork等等
    CNLabeledValue *addressLabel = [CNLabeledValue labeledValueWithLabel:CNLabelWork value:address];
    if (!exist) {
        contact.postalAddresses = @[addressLabel];
    }else{
        if ([contact.postalAddresses count] >0) {
            NSMutableArray *addresses = [[NSMutableArray alloc] initWithArray:contact.postalAddresses];
            [addresses addObject:addressLabel];
            contact.postalAddresses = addresses;
        }else{
            contact.postalAddresses = @[addressLabel];
        }
    }
}

四.一个未解决bug:
联系人编辑页面点击拍照,无法拍照,黑屏,我看微信也是这样的,还在找出问题的原因
  
 
五.总结
新的方法整体比之前更加面向对象了,不像之前这copy那copy的,好用了一些,但是联系人的属性实在太多了,代码写起来很慢,另外苹果也提供了新的联系人读写操作的方法,用起来也同样比之前的更好理解,主要类是CNSaveRequest和CNContactStore,用起来和FMDB很像,这里就不举例了,最后吐槽下苹果吧,现在iOS系统升级给老程序带来了很多的工作,我在做的就是一个三年前的老项目,本身就很庞大复杂,还不断在修改需求、加功能,真是被苹果玩吐血啊。

六.附赠我遇到的iOS9必改内容
1.国际化判断
2.高级的定位功能
3.https加密问题
4.老程序使用Default.png方式设置欢迎页面,无法获取屏幕高度
5.联系人api更换

时间: 2024-08-04 14:30:25

iOS9联系人保存详解的相关文章

Android中的ContactPicker(选择联系人) 详解

Android API: http://developer.android.com/reference/android/content/Intent.html ContactPicker:用户可以选择一个联系人. 1. 创建ContactPicker活动(Activity) 位置: java->package-> ContactPicker.java package mzx.spike.contactpicker.app; import android.app.Activity; import

【ANDROID游戏开发十三】(保存游戏数据 [下文])详解SQLITE存储方式,并把SQLITE的数据库文件存储在SD卡中!!!

本站文章均为 李华明Himi 原创,转载务必在明显处注明:  转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/android-game/329.html ----------------------- 『很多童鞋说我的代码运行后,点击home或者back后会程序异常,如果你也这样遇到过,那么你肯定没有仔细读完Himi的博文,第十九篇Himi专门写了关于这些错误的原因和解决方法,这里我在博客都补充说明下,省的童鞋们总疑惑这一块:请点击下面联系进入阅读:

【ANDROID游戏开发十二】(保存游戏数据 [上文])详解SHAREDPREFERENCE 与 FILEINPUTSTREAM/FILEOUTPUTSTREAM将数据存储到SD卡中!

本站文章均为 李华明Himi 原创,转载务必在明显处注明:  转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/android-game/327.html ----------------------- 『很多童鞋说我的代码运行后,点击home或者back后会程序异常,如果你也这样遇到过,那么你肯定没有仔细读完Himi的博文,第十九篇Himi专门写了关于这些错误的原因和解决方法,这里我在博客都补充说明下,省的童鞋们总疑惑这一块:请点击下面联系进入阅读:

实例详解Nodejs 保存 payload 发送过来的文件_node.js

1接受文件 http://stackoverflow.com/questions/24610996/how-to-get-uploaded-file-in-node-js-express-app-using-angular-file-upload 可以用下列的第三方库 •busboy and connect-busboy •multiparty and connect-multiparty •formidable •multer 2保存文件 psnodejs get/request 灌水评论示例

Android 获取手机联系人实例代码详解_Android

我的风格,废话不多说了,直接给大家贴代码了. 具体代码如下所示: package com.org.demo.demo; import com.org.wangfeng.R; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Butt

Activity配置、启动和关闭activity实例详解_Android

大家先看下效果图: Android为我们提供了四种应组件,分别为Activity.Service.Broadcast receivers和Content providers,这些组建也就是我们开发一个Android应用程序的基石.系统可以通过不同组建提供的切入点进入到开发的应用程序中.对用户来说不是所有的组建都是实际的切入点,但是他们之间都是相互依赖的,它们每一个作为存在的实体,扮演着特定的角色,作为独一无二的基石帮助开发者定义Android应用的行为.下面我将整理自己的Activity学习点滴

《Android 网络开发与应用实战详解》——2.3节Android系统架构

2.3 Android系统架构 Android 网络开发与应用实战详解 在本节内容中,将进一步分解Android应用程序,详细剖析Android应用程序的核心构成部分,为读者学习本书后面知识打下基础. 2.3.1 Android体系结构介绍 Android作为一个移动设备的平台,其软件层次结构包括操作系统(OS).中间件(MiddleWare)和应用程序(Application).根据Android的软件框图,其软件层次结构自下而上分为以下4层. (1)操作系统层(OS). (2)各种库(Lib

Android四大组件之Service(服务)实例详解_Android

本文实例讲述了Android四大组件之服务用法.分享给大家供大家参考,具体如下: 很多情况下,一些与用户很少需要产生交互的应用程序,我们一般让它们在后台运行就行了,而且在它们运行期间我们仍然能运行其他的应用. 为了处理这种后台进程,Android引入了Service的概念.Service在Android中是一种长生命周期的组件,它不实现任何用户界面. 基本概念 Ÿ   Service是一种在后台运行,没有界面的组件,由其他组件调用开始. Ÿ   创建Service,定义类继承Service,An

Android项目基本结构详解_Android

一.简介 第3章虽然通过百度地图应用展示了你可能感兴趣的内容,但是,如果你是一个初学者,一开始就看懂和理解代码可能会非常费劲.为了解决此问题,从这一章开始,本模块将从最基本的内容讲起,带你逐步进入用C#进行Android应用开发的乐园. 二.AndroidApp入口 要用C#开发Android应用程序,首先需要对项目的基本结构有一个感性认识.如下图所示: Android应用程序使用的是单一入口,源程序中并不能一眼看出程序从哪开始运行,当应用程序加载到内存中时,Android操作系统会自动从内部自