iOS 通讯录开发的装有姿势

手动安装

  1. 下载 LJContactManager 文件夹内的有内容。
  2. 将 LJContactManager 内的源文件添加(拖放)到公的工。
  3. 导入 LJContactManager.h

关于Xcode的文本夹如何创建

Xcode中组(Group) 和 文件夹引用(Folder Reference)的区分

刚说了有的有关目录结构设计事情,现在说说 Xcode
的文件夹。我看罢无数路,我当相当一部分的档次之公文夹管理不客观。在Xcode中
项目的文件夹有 Group 和 Folder Reference 之分。它们的别在 Xcode
Groups vs. Folder
References
这篇稿子里有详细的叙述。

组和文件夹引用

Group 的毛病如下:

1)Xcode 会为每个文件创建一个 reference,存储在 project.pbxproj
文件被,当起差不多个 target 时,每个文件的 reference
会被复制多单,这就见面大大加 project.pbxproj
文件的尺码及复杂度,这对于代码版本管理来是个头疼的题材,尤其是逢 merge
conflict 的时节。
2)项目面临 Group
的构造与磁盘上之文件价的组织可以视为没有吗关系之,在磁盘上的一个文本在一个某部文件夹着,但是于
Xcode 的型组织中,它可能当另外 Group
中,这样想只要错过搜寻对应的文件就常叫人不胜晕。
3)如果您以 Xcode 之外直接去 Finder 里走类文件及不同之目时,那么在
Xcode 中针对是文件的 reference 就会见坏掉。
Group 的长如下:

1)你得选取磁盘上的文本上加至路遭到,不思量如果的莫上加就是推行了。
2)对两样之 target 对应的文件能够更好地管理,比如,你可择针对性一个
target 排除有一个文件。
3)当 build 的早晚,Xcode 会把拥有的 Group 下的文本还放至 bundle
的一等目录,所以若调用文件时不需要制订它的具体位置,比如,你不要如此
[UIImage imageNamed:@”Images/Icons/Go”]; ,这之所以这样虽可 [UIImage
imageNamed:@”Go”];,但是就虽象征当任何项目遭到,你莫能够有同名的公文了。
Folder Reference 的产生这些亮点:

1)Xcode 只存储 folder 的 reference,这个 folder 下所有的文件和
subfolder
都见面自动添加至品种被失。这会使项目文件再度有些更简便,代码版本管理时,产生
merge conflict 的恐怕就是又不见。
2)如果您以文件系统中一直对 folder 下的文件进行修改、移动还是去,Xcode
会自动更新对应之 folder reference
来反应这些改动,这样管理类文件也再也简便易行。
3)项目之构造与 folder 在磁盘的构造是平等的,这样便无会见晕菜了。
4)由于在不同之 folder 路径,你便无欲担心文件重名问题了,因为以
build 的上,文件夹结构也会见吃平放 bundle 中失。
Folder Reference 的产生这些弱点:

1)对两样之 target 的治本是独灾难,因为一个 folder
下之代码或文件,要么全同或全不要。当然,如果你能啊歧之 target
去立不同的 Folder Reference,这看起也无什么不好的。
2)对 folder 下之文书无法隐藏,磁盘上而此 folder
下有这文件,那么以档次结构被即见面相它。
3)在加载文件资源的时,你得制定全路线。也就是说,你得这样:[UIImage
imageNamed:@”Images/Icons/Go”];。
4)存储于 Folder Reference 中的图在 Interface Builder
中使用时会见遇到各种问题。
当实际上采用受到,使用 Group 要多得多。

Xcode 项目组织及磁盘文件结构的附和

方说了 Group 和 Folder Reference
各自的得失,在品种中,我习惯及啊是勿使用 Folder Reference,只所以
Group。在 Xcode 项目遭到创造 Group 的措施产生少数种植:

1)第一栽方法:创建时要事先以磁盘上缔造对应的文本夹,再管公文夹拖上
Xcode 项目受到对应的岗位,并精选 Create groups for any added
folders。这样创建的 Group 会对诺着磁盘上的 Folder。

2)第二种植方式:直接当 Xcode 项目面临开创 Group。

对照 Xcode 项目的 MyProject.xcodeproj/project.pbxproj 文件可以看到相应着
Folder 的 Group 和直接开立的 Group 的别就是在于前者是为此 path
属性去记录,后者是因此 name 属性去记录。如下,Helper 是一个怪应 Folder
的 Group,Resource 是一个对应 Folder 的
Group。通过逐条结点的父子关系以及 path 属性,Xcode 就可知管理好每个文件的
Reference。

//MyProject.xcodeproj/project.pbxproj

45E59EDF18BBA92C00251797 /* Helper */ = {
     isa = PBXGroup;
     children = (
          452183D3195AA18F00679F14 /* CXTaskControlService.h */,
          452183D4195AA18F00679F14 /* CXTaskControlService.m */,
     );
     name = Helper;
     sourceTree = "<group>";
};
45E59EE318BC2B4100251797 /* Resource */ = {
     isa = PBXGroup;
     children = (
          45E59EE418BC2B4100251797 /* Image */,
          45C97CA91900260A0020C517 /* Sound */,
     );
     path = Resource;
     sourceTree = "<group>";
};

对于第一种方式:

好处:这样磁盘上之布局以及 Xcode 中的门类布局即是逐一对应之。让 Xcode
的目结构能和磁盘文件的组织保持一致,这样为以寻找代码文件的时刻能够又清晰。

坏处:创建和保管代码文件时将麻烦一些。当您于 Xcode
项目面临管一个文书由一个索引拖动到另外一个索引中常,它在 Xcode
中形的目路径改变了,但是其当磁盘上的大体位置并无来变动,这便会见促成杂乱。所以,为了保障对承诺提到,当您一旦改变文件的目录时,你要手动到
Finder 文件夹着失去运动文件,再在 Xcode
项目蒙失去相应的文本引用再重复长到新的目下。这还要带动了另外一个题材,就是
git
对文本之本子管理信息会给毁掉,你恐怕无法看到是文件前的版本了,这个代价不过就充分了。所以,如果假定用版本管理,就要好好考虑这元素了。

对于第二种方式:

好处:直接在 Xcode
中管理类,添加、删除、移动还不行有益于。采用版本管理时,代码文件的版本信息不见面因运动如果丢掉。

坏处:Xcode 中的门类布局以及磁盘上之布局不能够挨个对应。
冲地方的对比,

自己的建议:对于一贯的只是复用代码可以下第一种艺术,因为也未会见时时倒。复用时,挪动起来可以操作;对于特别的目结构,比如一级目录:Utilities、Helpers、Features、Resource
等目录可以利用第一种植方式。其他的图景统统以第二种方法尽管尽了。

实现步骤

同一、创建选择联系人之控制器

// 创建联系人选择控制器    
ABPeoplePickerNavigationController *pvc = [[ABPeoplePickerNavigationController alloc] init];

仲、设置代理(用来接受用户选择的牵连人消息)

// 设置代理
pvc.peoplePickerDelegate = self;

老三、弹出联系人控制器

if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined)
{
   ABAddressBookRef bookRef = ABAddressBookCreate();
   ABAddressBookRequestAccessWithCompletion(bookRef, ^(bool granted, CFErrorRef error) {
       if (granted)
       {
           NSLog(@"授权成功!");
           [self presentViewController:pvc animated:YES completion:nil];
       }
       else
       {
           NSLog(@"授权失败!");
       }
   });
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized)
{
   [self presentViewController:pvc animated:YES completion:nil];
}

季、实现代理方

// 选择某个联系人时调用
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person
{
    NSLog(@"选中联系人");
    CFStringRef firstName = ABRecordCopyValue(person, kABPersonFirstNameProperty);
    CFStringRef lastName = ABRecordCopyValue(person, kABPersonLastNameProperty);

    NSString *fir = CFBridgingRelease(firstName);
    NSString *las = CFBridgingRelease(lastName);

    NSLog(@"%@---%@", fir, las);

    ABMultiValueRef multi = ABRecordCopyValue(person, kABPersonPhoneProperty);
    CFIndex count = ABMultiValueGetCount(multi);
    for (int i = 0; i  < count; i++) 
    {
        NSString *label = (__bridge_transfer NSString *)ABMultiValueCopyLabelAtIndex(multi, i);
        NSString *phone =(__bridge_transfer NSString *)  ABMultiValueCopyValueAtIndex(multi, i);
        NSLog(@"%@---%@", label, phone);
    }
}

Core Foundation 对象手动管理内存,如果是 Create、Copy、Retain
等字样创建的对象,需要手动 CFRelease。类似 Objective-C 的 MRC。

拓展:__bridge__bridge_retained__bridge_transfer
三单转移关键字之界别。

  1. __bridge 只做类型转换,但是非改动对象(内存)管理权;
  2. __bridge_retained(也足以以CFBridgingRetain)将 Objective-C
    的目标转换为 Core Foundation
    的目标,同时将目标(内存)的管理权交给我们,后续要动用 CFRelease
    或者有关方来刑满释放对象;
  3. __bridge_transfer(也得采用 CFBridgingRelease )将 Core
    Foundation 的靶子转换为 Objective-C
    的靶子,同时以对象(内存)的管理权交给 ARC。

五、在相应的代办方中获得联系人消息

// 1.选择联系人时使用(不展开详情)
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person;

// 2.选择联系人某个属性时调用(展开详情)
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier;

// 3.取消选中联系人时调用
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker;

在意:选择联系人之非进行详情(代理方1)和开展详情(代理方2)的代办方还写了底下,展开详情的代理方就是不履。

一个好之工文件组织结构设计的优点

1.工程文件分类明确,文件容易招来

2.目录功能定义清晰,工程师能自由理解在啊创建新文件,尤其是当组织来上秤座的工程师经常

3.工与磁盘文件结构清晰,代码维护好

4.目录粒度足够精细,减少项目依赖关系和多模块的可移植性

……

最后

是因为作者水平有限,文中如果起错的地方,或者有再次好之艺术,还往大神指出。
屈居本文的所有 demo
下充斥链接,【GitHub】。
而您看了晚以为对而所有帮助,还为在 GitHub 上接触个
star。赠人玫瑰,手有余香。

自我现在档之工结构设计样例

废话不多说,最后为大家举一个己现在种一般以工程文件设计布局的事例吧

1.主目录结构
-ProjectDemo
    --Features         //模块。包含各个模块的Model,View,Controller,Manager
    --categories            //类目。包含各种类的分类
    --Frameworks        //系统框架。包含导入的系统的框架
    --Helpers           //帮助类。包含网络,数据库,归档,定位等操作类的封装和实现
    --Utilites       //工具类,一些非对象的,而是类方法调用的类
    --Vendors           //第三方库。部分需要修改或者不支持cocoapod的第三方的框架引入
    --Config                //配置。包含宏定义文件,全局配置文件,全局常量文件,颜色配置文件
    --Resources         //资源。包含plist,image,html,bundle,Localizable.strings等
    --AppEntry          //程序入口。包含AppDelegate,main.c,info.plist
-PAHealthTests
-PAHealthUITests
-Products           // 系统自动生成的.app所在文件夹
-Pods                   // 采用 CocoaPods 管理的第三方库。

2.模块目录结构
-- Features         
    ---Base             //MVC的基类或者通用类
        ----Models      //数据模型
        ----Views       //视图
        ----Controllers //控制器
        ----Manager     //store层的数据管理类
    ---Home
        ----Models
        ----Views
        ----Controllers
        ----Manager
    ---UserCenter
        ----Models
        ----Views
        ----Controllers
        ----Manager
    ---UserEntry
        ----Models
        ----Views
        ----Controllers
        ----Manager

    ---Payment
        ----Models
        ----Views
        ----Controllers
        ----Manager
    …

CNContactUI

开场

编程是同一山头艺术生活,不同意?嘿嘿,那不用生跟你翻两个白。没关系,你得会容许这句话:艺术品的一个会同主要且必须有特征就是是它们都是出于艺术家创作出的O(∩_∩)O~。方才小生我是挑起你玩的,讲真艺术品是由于艺术家创作是没其他问题之,当然其实艺术品还有一个特性就是是它把主要之底细发挥到最好致。所以,作为ios架构师,不加大了任何一个得提升项目质量、效率、健壮性等等的技术手段,也便是劈内事了。也许是不足够有技术含量,,发现国内外鲜有关于工程文件组织结构设计的文章。但是于自身背了之几只类别来拘禁,一个吓的工程文件组织结构会起源头上晋级工程师的开销效率,提升项目之健壮性和可扩展性,而自我哉掌握的看看了一些品类以糟糕的文件目录设计,导致代码管理以及档次发展进程会带来的问题。

关系人属性定义

享有的性能常量值都定义在了 ABPerson.h 头文件中。

联系人属性包括以下项目:

  1. 简简单单属性:姓、名当
  2. 大抵重属性:电话号码、电子邮件等
  3. 整合属性:地址等

注意:使用 ABRecordCopyValue 可以从同长达 Person
记录被获得到对应之记录,但是后续处理则要根据记录之实际品种加以区分。

关于Helper与Utility文件夹

自家看了无数品种,包括好与了之一对色要没Helper,要么没Utility文件夹,而是以她们混合于齐,傻傻分不清楚。
自家之提议:尽量细粒度化项目目录结构,在品种里分别提供helper目录和utility目录给开发者使用。与工作无关,具有对象性质,提供支撑力量的代码放到Helper,比如创建一个自定义对象的卷入。如果只是属于函数或算法,不是目标又多地方会用到,就停放Utility,比如排序/加密算法。

通讯录的改回调

// 注册通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_contactStoreDidChange) name:CNContactStoreDidChangeNotification object:nil]            

// 处理收到通知的 Action
- (void)_contactStoreDidChange
{
}

- (void)dealloc
{   
    // 注销通知
    [[NSNotificationCenter defaultCenter] removeObserver:self name:CNContactStoreDidChangeNotification object:nil];
}

当 App
活跃(前台+后台活动中)的时,当通讯录修改的上,会收到通知
当 App 不欢的时候(挂于的时节),App 收不顶通报;而是,当 App
到前台的早晚收延迟的通报。

参照文章

iOS项目之目结构
iOS应用架构谈
开篇

得到有的关系人数量

// 获取所有联系人记录
CFArrayRef array = ABAddressBookCopyArrayOfAllPeople(addressBook);
NSInteger count = CFArrayGetCount(array);

for (NSInteger i = 0; i < count; ++i) {
    // 取出一条记录
    ABRecordRef person = CFArrayGetValueAtIndex(array, i);

    // 取出个人记录中的详细信息
    // 名
    CFStringRef firstNameLabel = ABPersonCopyLocalizedPropertyName(kABPersonFirstNameProperty);
    CFStringRef firstName = ABRecordCopyValue(person, kABPersonFirstNameProperty);
    CFStringRef lastNameLabel = ABPersonCopyLocalizedPropertyName(kABPersonLastNameProperty);
    // 姓
    CFStringRef lastName = ABRecordCopyValue(person, kABPersonLastNameProperty);

    NSLog(@"%@ %@ - %@ %@", lastNameLabel, lastName, firstNameLabel, firstName);
}

至于MVC架构的以工程中简单种体现形式

1.出很多档是直接在工程里创建Controllers、Views、Models这样的目录结构…这样分类
2.依
Feature来划分,每个Feature里面有Controllers、Views、Models…这样分类

ios开发分工有点儿栽,一种植是分支开发,第一种植形式适合分层开发,一栽是分feature开发,第二栽样式适合分feature开发。分层开发通常是有人背DAO层(数据存取)开发,有人当控制器和view层的支付,但是当API尚未形成,而且Dao层同事对业务层关注不够,DAO层开发之的同事就净需要借助自身的想象力去规划数据存取的接口,而数就与骨子里API提供的接口存在巨大的落差,从而造成分层开发的各层都见面做出大量代码重构。所以分feature开发是大家比较容易接受的平等栽开发方式。

之所以我之提议:是下第二种MVC组织形式,如果您采取MVVM或者其它,也可以参见这思路。其实首先种形式还有一个问题,比如你要寻找ControllerA的TableView用到之cellB类,你还要去Views里面一个个追寻,太痛苦了,就算search也要苦毕竟非能够所见即所得。而分feature则对于负责这feature的付出工程师找到关于文件则容易之基本上矣。

AddressBookUI

关于Common文件夹

本条在大神casa的文章《iOS应用架构谈
开篇》已经生鲜明的象征,在他的篇章中他提议以工程成功不要发Common文件夹。我一直以来可能都践行细粒度的目录结构划分方式,所以是因为我肩负架构的种还无过common文件夹。我呢比较认同casa的眼光,不仅仅是针对common文件夹,其实前面提到的helper和utility文件夹之所以拆分开也是因梦想能够细致粒度拆分模块,减少横向依赖。

Common的补只出一个,就是头特别省事儿。然而其的流弊比好处要多尽多。

Common不仅仅是一个文件夹,它也会见是一个Pod。不管是啊,在Common里面颇容易形成错综复杂的微模块依赖,在模块成长过程中,会纵容工程师不放在心上依赖之管理,乃至于将来如果假定以模块拆分出去,会坏之艰难。
Common本身和细粒度模块设计的思想背道而驰,属于同一栽不正好的偷懒手段,在明天工作拓张会变成阻止。
只要设置了Common,就等吃地狱的法家打开了一个小缝,每次业务迭代都见面产生部分不顶好分类的物放入Common,这即于保安Common的人口带了颇好之工作量,而且这些工作量全都是体力活,非常容易出错。

自身建议于档次遭到莫采用Common文件夹。这样工程师在开立有共有文件,就亟须遵他们的任务将他们分开及不同模块里去,而未是偷懒,扔到common里,这样做虽然早期大难,但是针对后期维护、扩展、移植将牵动巨大的便宜。你会意识型之可维护性大大提高,模块升级后要召开的合工作深轻松,解放了挺苦逼的Common维护者,更多的时光可以用当重本质的支出工作达。这副本人移植强调的细粒度模块划分的架思想。

兑现步骤

一致、创建选择联系人之控制器

// 创建联系人选择控制器    
CNMutableContact *contact = [[CNMutableContact alloc] init];
CNLabeledValue *labelValue = [CNLabeledValue labeledValueWithLabel:CNLabelPhoneNumberMobile
ontact.phoneNumbers = @[labelValue];                                                                     value:[CNPhoneNumber phoneNumberWithStringValue:phoneNum]];
CNContactViewController *contactController = [CNContactViewController viewControllerForNewContact:contact];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:contactController];

仲、设置代理(用来接收用户选择的联络人信息)

// 设置代理
contactController.delegate = self;

其三、弹出联系人控制器

[controller presentViewController:nav animated:YES completion:nil];

季、实现代理方

// 选择某个联系人时调用
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty
{
    CNContact *contact = contactProperty.contact;
    NSString *name = [CNContactFormatter stringFromContact:contact style:CNContactFormatterStyleFullName];
    CNPhoneNumber *phoneValue= contactProperty.value;
    NSString *phoneNumber = phoneValue.stringValue;
    NSLog(@"%@--%@",name, phoneNumber);
}

五、在相应的代办方吃取联系人消息

// 1.选择联系人时使用(不展开详情)
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContact:(CNContact *)contact;

// 2.选择联系人某个属性时调用(展开详情)
- (void)contactPicker:(CNContactPickerViewController *)picker didSelectContactProperty:(CNContactProperty *)contactProperty;

// 3.取消选中联系人时调用
- (void)contactPickerDidCancel:(CNContactPickerViewController *)picker;

只顾:与 AddressBookUI
一样,选择联系人之非开展详情(代理方1)和进展详情(代理方2)的代理方还写了之下,展开详情的代理方就是不实行。

iOS 9 以后的通讯录新框架

iOS 9 之前操作通讯录还是比麻烦的,iOS 9
以后苹果推出了全新的通讯录框架,使用起来越的面向对象。

CocoaPods

  1. 在 Podfile 中添加 pod 'LJContactManager'
  2. 执行 pod installpod update
  3. 导入 <LJContactManager.h>。

AddressBook

一律、请求授权

自从 iOS 6
开始,需要获得用户的授权才会顾通讯录,因此在使之前,需要检查用户是否曾授权。

// 获得通讯录的授权状态
ABAddressBookGetAuthorizationStatus()

授权状态

  1. 用户还从未控制是否授权你的次第进行走访:kABAuthorizationStatusNotDetermined

  2. iOS
    设备上部分认可配置阻止程序与通讯录数据库进行相互:kABAuthorizationStatusRestricted

  3. 用户明显的拒绝了若的次序对通讯录的看:kABAuthorizationStatusDenied

  4. 用户既授权为你的程序对通讯录进行走访:kABAuthorizationStatusAuthorized

// 判断当前的授权状态是否是用户还未选择的状态
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) 
{
   ABAddressBookRef bookRef = ABAddressBookCreate();
   ABAddressBookRequestAccessWithCompletion(bookRef, ^(bool granted, CFErrorRef error) {
       if (granted) 
       {
           NSLog(@"授权成功!");
       }
       else
       {
           NSLog(@"授权失败!");
       }
   });
}

次、判断授权状态

只要就授权,则继续;未授权,则提醒用户,并回到。

// 判断当前的授权状态
if (ABAddressBookGetAuthorizationStatus() != kABAuthorizationStatusAuthorized) 
{
    NSLog(@"您的通讯录暂未允许访问,请去设置->隐私里面授权!");
    return;
}

其三、创建通讯录对象

// 创建通讯录对象
ABAddressBookRef bookRef = ABAddressBookCreate();

季、从通信录对象被, 获取有的关系人

// 获取通讯录中所有的联系人
CFArrayRef arrayRef = ABAddressBookCopyArrayOfAllPeople(bookRef);

五、遍历所有的沟通人

// 遍历所有联系人
CFIndex count = CFArrayGetCount(arrayRef);
for (int i = 0; i < count; i++) 
{
   ABRecordRef record = CFArrayGetValueAtIndex(arrayRef, i);

   // 获取姓名
   NSString *firstName = (__bridge_transfer NSString *)ABRecordCopyValue(record, kABPersonFirstNameProperty);
   NSString *lastName = (__bridge_transfer NSString *)ABRecordCopyValue(record, kABPersonLastNameProperty);
   NSLog(@"firstName = %@, lastName = %@", firstName, lastName);

   // 获取电话号码
   ABMultiValueRef multiValue = ABRecordCopyValue(record, kABPersonPhoneProperty);
   CFIndex count = ABMultiValueGetCount(multiValue);
   for (int i = 0; i < count; i ++) 
   {
       NSString *label = (__bridge_transfer NSString *)ABMultiValueCopyLabelAtIndex(multiValue, i);
       NSString *phone = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(multiValue, i);
       NSLog(@"label = %@, phone = %@", label, phone);
   }

   CFRelease(multiValue);
}

六、释放不再行使的靶子

CFRelease(bookRef);
CFRelease(arrayRef);

兑现步骤

平等、请求授权

// 获得通讯录的授权状态
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts]

授权状态

  1. 用户还尚无决定是否授权你的顺序开展走访:CNAuthorizationStatusNotDetermined

  2. iOS
    设备及一些准配置阻止程序和通讯录数据库进行互:CNAuthorizationStatusRestricted

  3. 用户明显的不肯了而的程序对通讯录的看:CNAuthorizationStatusDenied

  4. 用户就授权给您的先后对通讯录进行访问:CNAuthorizationStatusAuthorized

// 判断当前的授权状态是否是用户还未选择的状态
if (status == CNAuthorizationStatusNotDetermined)
{
    CNContactStore *store = [CNContactStore new];
    [store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
        if (granted) 
        {
            NSLog(@"授权成功!");
        }
        else
        {
            NSLog(@"授权失败!");
        }
    }];
}

次、判断授权状态

一旦已授权,则继续;未授权,则提醒用户,并回。

// 判断当前的授权状态
if (status != CNAuthorizationStatusAuthorized) 
{
    NSLog(@"您的通讯录暂未允许访问,请去设置->隐私里面授权!");
    return;
}

其三、创建通讯录对象

// 创建通讯录对象
CNContactStore *contactStore = [CNContactStore new];

季、设置访问的特性 Key,每个 Key 对应一个属性,iOS 9
新增,如果没有设置,访问该属性就会倒。

// 姓名前缀
CNContactNamePrefixKey     
// 名                 
CNContactGivenNameKey                       
// 中间名
CNContactMiddleNameKey  
// 姓                   
CNContactFamilyNameKey            
// 婚前姓         
CNContactPreviousFamilyNameKey
// 姓名后缀
CNContactNameSuffixKey   
// 昵称                   
CNContactNicknameKey                        
// 公司
CNContactOrganizationNameKey                
// 部门
CNContactDepartmentNameKey                  
// 职位
CNContactJobTitleKey                        
// 名字拼音或音标
CNContactPhoneticGivenNameKey
// 中间名拼音或音标              
CNContactPhoneticMiddleNameKey
// 姓拼音或音标
CNContactPhoneticFamilyNameKey  
// 公司拼音或音标            
CNContactPhoneticOrganizationNameKey      
// 生日  
CNContactBirthdayKey   
// 农历                    
CNContactNonGregorianBirthdayKey    
// 备注        
CNContactNoteKey                            
// 图片
CNContactImageDataKey                       
// 缩略图
CNContactThumbnailImageDataKey              
// 图片是否允许访问
CNContactImageDataAvailableKey              
// 类型
CNContactTypeKey                            
// 号码
CNContactPhoneNumbersKey                    
// 电子邮件
CNContactEmailAddressesKey                  
// 地址
CNContactPostalAddressesKey                 
// 日期
CNContactDatesKey   
// URL                        
CNContactUrlAddressesKey                    
// 关联人
CNContactRelationsKey                       
// 社交
CNContactSocialProfilesKey                  
// 即时通讯
CNContactInstantMessageAddressesKey         

NSArray *keys = @[CNContactPhoneNumbersKey,CNContactGivenNameKey];

五、从通信录对象被, 获取具有的维系人,并遍历

// 获取通讯录中所有的联系人
CNContactFetchRequest *request = [[CNContactFetchRequest alloc] initWithKeysToFetch:keys];

[contactStore enumerateContactsWithFetchRequest:request error:nil usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {
    // 获取姓名
    NSString *firstName = contact.familyName;
    NSString *lastName = contact.givenName;

    NSLog(@"%@--%@",firstName,lastName);

    // 获取电话号码

    for (CNLabeledValue *labeledValue in contact.phoneNumbers)
    {
         CNPhoneNumber *phoneValue = labeledValue.value;
         NSString *phoneNumber = phoneValue.stringValue;
         NSString *label = [CNLabeledValue localizedStringForLabel:labeledValue.label];
         NSLog(@"%@--%@",label,phoneNumber);
    }

}];

长维系人之手续

加上沟通人的步骤:

  1. 通过 ABPersonCreate 函数创建一个初的联系人(返回 ABRecordRef)。
  2. 通过 ABRecordSetValue 函数设置联系人之习性。
  3. 通过 ABAddressBookAddRecord 函数将关系人填补加至通讯录数据库中。
  4. 通过 ABAddressBookSave 函数保存刚才所作的改。

得经过 ABAddressBookHasUnsavedChanges 函数判断是否发生无保存之改动
当控制是否改变通讯录数据库后,你可以分别以 AbAddressBookSave
ABAddressBookRevert 方式来保存还是放弃双重改 。

使用

着重提供以下的点子:

  • 慎选联系人

/**
 选择联系人

 @param controller 控制器
 @param completcion 回调
 */
- (void)selectContactAtController:(UIViewController *)controller
                      complection:(void (^)(NSString *name, NSString *phone))completcion;
  • 始建新关系人

/**
 创建新联系人

 @param phoneNum 手机号
 @param controller 当前 Controller
 */
- (void)createNewContactWithPhoneNum:(NSString *)phoneNum controller:(UIViewController *)controller;
  • 长到存活联系人

/**
 添加到现有联系人

 @param phoneNum 手机号
 @param controller 当前 Controller
 */
- (void)addToExistingContactsWithPhoneNum:(NSString *)phoneNum controller:(UIViewController *)controller;
  • 抱联系人列表(未分组的通讯录)

/**
 获取联系人列表(未分组的通讯录)

 @param completcion 回调
 */
- (void)accessContactsComplection:(void (^)(BOOL succeed, NSArray <LJPerson *> *contacts))completcion;
  • 落联系人列表(已分组的通讯录管理)

/**
 获取联系人列表(已分组的通讯录)

 @param completcion 回调
 */
- (void)accessSectionContactsComplection:(void (^)(BOOL succeed, NSArray <LJSectionPerson *> *contacts, NSArray <NSString *> *keys))completcion;
  • 报道录变更回调(未分组的通讯录)

/**
 通讯录变更回调(未分组的通讯录)
 */
@property (nonatomic, copy) void (^contactsChangeHanlder) (BOOL succeed, NSArray <LJPerson *> *newContacts);
  • 简报录变更回调(已分组的通讯录)

/**
 通讯录变更回调(已分组的通讯录)
 */
@property (nonatomic, copy) void (^sectionContactsHanlder) (BOOL succeed, NSArray <LJSectionPerson *> *newSectionContacts, NSArray <NSString *> *keys);

LJContactManager

通讯录的修改回调

// 创建通讯录
self.addressBook = ABAddressBookCreate();  
// 注册通知  
ABAddressBookRegisterExternalChangeCallback(self.addressBook, _addressBookChange, nil);            

// 处理收到通知的 Action
void _addressBookChange(ABAddressBookRef addressBook, CFDictionaryRef info, void *context)
{
}

- (void)dealloc
{
    // 注销通知
    ABAddressBookUnregisterExternalChangeCallback(self.addressBook, _addressBookChange, nil);
    // 释放对象
    CFRelease(self.addressBook);
}

操作联系人的头像

怀念操作联系人的头像,有以下函数
BPersonHasImageData
看清通讯录中的联系人是否有图表

ABPersonCopyImageData
获图片数(假如有言语)

ABPersonSetImageData
安联系人的图纸数

大多重属性

联系人的多少属性值就没有这么简单,一个性能可能会见蕴藏多独价值
照邮箱,分为工作邮箱、住宅信箱、其他邮箱等
本电话,分为工作电话、住宅电话、其他电话等
若果是繁体属性,那么 ABRecordCopyValue 函数回的饶是
ABMultiValueRef 类型的数目,例如邮箱或者电话

// 取电话号码
ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
// 取记录数量
NSInteger phoneCount = ABMultiValueGetCount(phones);
// 遍历所有的电话号码
for (NSInteger i = 0; i < phoneCount; i++) 
{

}

介绍

LJContanctManager 是我形容的同一舒缓操作通讯录的类库,iOS 9 之前以的是
AddressBook 和 AddressBookUI 系统库,iOS 9 之后采用苹果新出的 Contacts
和 ContactsUI 框架。

CoreFoundation 及 Foundation之间的桥接

// 1. 获取通讯录引用
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, nil);
// 2. 获取所有联系人记录
NSArray *array = (__bridge NSArray *)(ABAddressBookCopyArrayOfAllPeople(addressBook));
for (NSInteger i = 0; i < array.count; i++) {
    // 取出一条记录
    ABRecordRef person = (__bridge ABRecordRef)(array[i]);
    // 取出个人记录中的详细信息
    NSString *firstNameLabel = (__bridge NSString *)(ABPersonCopyLocalizedPropertyName(kABPersonFirstNameProperty));
    NSString *firstName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonFirstNameProperty));
    NSString *lastNameLabel = (__bridge NSString *)(ABPersonCopyLocalizedPropertyName(kABPersonLastNameProperty));
    NSString *lastName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonLastNameProperty));
    NSLog(@"%@ %@ - %@ %@", lastNameLabel, lastName, firstNameLabel, firstName);
}
CFRelease(addressBook);

通讯录简介

通讯录使用状况:

  1. 电商类的 App,设置收货人电话号码。
  2. 即时通讯类 App,添加手机联系人好友。

通讯录获取方案:

相同、iOS 9 以前的通讯录框架

  1. AddressBookUI.framework 框架

    1. 供了关联人列表界面、联系人详情界面、添加联系人界面等。
    2. 相似用于选择联系人。
  2. AddressBook.framework 框架

    1. 纯 C 语言的 API,仅仅是得联系人数仍。
    2. 从不供 UI 界面展示,需要协调搭建沟通人显示界面。
    3. 其中的数据类型大部分冲 Core Foundation
      框架,使用起来炒鸡复杂。

第二、 iOS 9 以后最新通讯录框架

  1. ContactsUI.framework 框架。

    • 拥有 AddressBookUI.framework
      框架的有功能,使用起来更为的面向对象。
  2. Contacts.framework 框架。

    • 拥有 AddressBook.framework 框架的所有机能,不再是 C 语言的
      API,使用起来非常简单。

iOS 9 以前的通讯录框架

安装

获复杂属性的章程

// 电话标签
CFStringRef phoneLabel = ABMultiValueCopyLabelAtIndex(phones, i);
// 本地化电话标签
CFStringRef phoneLocalLabel = ABAddressBookCopyLocalizedLabel(phoneLabel);
// 电话号码
CFStringRef phoneNumber = ABMultiValueCopyValueAtIndex(phones, i);

粗略属性

一个关联人就算是一个
ABRecordRef,每个联系人还来协调的性质,比如名、电话、邮件等。
使用 ABRecordCopyValue 函数可以从 ABRecordRef
中获得联系人之简易属性(例如:一个字符串)。
ABRecordCopyValue 函数接收 2 单参数。
第 1 只参数是 ABRecordRef 实例。
第 2 独参数是性关键字,定义在 ABPerson.h 中。
ABPersonCopyLocalizedPropertyName
函数可以根据指定的重中之重字取相应的价签文本。

CNContact

添加群组的手续

增长群组的步骤大体与增长联络官一致:

  1. 通过 ABPersonCreate 函数创建一个新的组。(返回 ABRecordRef
  2. 通过 ABRecordSetValue 函数设置组名。
  3. 通过 ABAddressBookAddRecord 函数将组添加到通讯录数据库被。
  4. 通过 ABAddressBookSave 函数保存刚才所发的修改。

Post Author: admin

发表评论

电子邮件地址不会被公开。 必填项已用*标注