iOS之短信,郵件,通信錄技術(shù)的完美實(shí)現(xiàn)

前言
今天給大家詳細(xì)介紹一下iOS之短信,郵件,通信錄技術(shù)的具體實(shí)現(xiàn)

  1. 系統(tǒng)應(yīng)用之短信,郵件
    在開(kāi)發(fā)某些應(yīng)用時(shí)可能希望能夠調(diào)用iOS系統(tǒng)內(nèi)置的電話、短信、郵件、瀏覽器應(yīng)用,此時(shí)你可以直接使用UIApplication的OpenURL:方法指定特定的協(xié)議來(lái)打開(kāi)不同的系統(tǒng)應(yīng)用。常用的協(xié)議如下:
    打電話:tel:或者tel:、telprompt:或telprompt:(撥打電話前有提示)
    發(fā)短信:sms:或者sms:
    發(fā)送郵件:mailto:或者mailto:
    啟動(dòng)瀏覽器:http:或者h(yuǎn)ttp:
    下面以一個(gè)簡(jiǎn)單的demo演示如何調(diào)用上面幾種系統(tǒng)應(yīng)用:
    #import "ViewController.h"
    @interface ViewController ()
    @end
    @implementation ViewController
    - (void)viewDidLoad {
    [super viewDidLoad];
    }
    #pragma mark - UI事件//打電話
    - (IBAction)callClicK:(UIButton *)sender {
    NSString *phoneNumber=@"18500138888";
    NSString *url=[NSString stringWithFormat:@"tel:%@",phoneNumber];

      //這種方式會(huì)直接撥打電話 
      NSString *url=[NSString stringWithFormat:@"telprompt:%@",phoneNumber];
      //這種方式會(huì)提示用戶確認(rèn)是否撥打電話
     [self openUrl:url];}
    
     //發(fā)送短信
     - (IBAction)sendMessageClick:(UIButton *)sender {
         NSString *phoneNumber=@"18500138888";
         NSString *url=[NSString stringWithFormat:@"sms://%@",phoneNumber]; 
         [self openUrl:url];
     }
    
     //發(fā)送郵件
     - (IBAction)sendEmailClick:(UIButton *)sender {
         NSString *mailAddress=@"kenshin@hotmail.com";
         NSString *url=[NSString stringWithFormat:@"mailto:%@",mailAddress]; [self openUrl:url];
     }
    
     //瀏覽網(wǎng)頁(yè)
     - (IBAction)browserClick:(UIButton *)sender {
         NSString *url=@"http://www.cnblogs.com/kenshincui"; [self openUrl:url];
     }
      #pragma mark - 私有方法
     -(void)openUrl:(NSString *)urlStr{
      //注意url中包含協(xié)議名稱,iOS根據(jù)協(xié)議確定調(diào)用哪個(gè)應(yīng)用,例如發(fā)送郵件是“sms://”其中“//”可以省略寫(xiě)成“sms:”(其他協(xié)議也是如此) 
         NSURL *url=[NSURL URLWithString:urlStr]; 
         UIApplication *application=[UIApplication sharedApplication];
         if(![application canOpenURL:url]){ 
             NSLog(@"無(wú)法打開(kāi)\"%@\",請(qǐng)確保此應(yīng)用已經(jīng)正確安裝.",url); 
             return;
         } 
         [[UIApplication sharedApplication] openURL:url];
     }
     @end
    

    不難發(fā)現(xiàn)當(dāng)openURL:方法只要指定一個(gè)URL Schame并且已經(jīng)安裝了對(duì)應(yīng)的應(yīng)用程序就可以打開(kāi)此應(yīng)用。當(dāng)然,如果是自己開(kāi)發(fā)的應(yīng)用也可以調(diào)用openURL方法來(lái)打開(kāi)。假設(shè)你現(xiàn)在開(kāi)發(fā)了一個(gè)應(yīng)用A,如果用戶機(jī)器上已經(jīng)安裝了此應(yīng)用,并且在應(yīng)用B中希望能夠直接打開(kāi)A。那么首先需要確保應(yīng)用A已經(jīng)配置了Url Types,具體方法就是在plist文件中添加URL types節(jié)點(diǎn)并配置URL Schemas作為具體協(xié)議,配置URL identifier作為這個(gè)URL的唯一標(biāo)識(shí),如下圖![](http://upload-images.jianshu.io/upload_images/1663804-9d3c0ae6772c3d6b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)然后就可以調(diào)用openURL方法像打開(kāi)系統(tǒng)應(yīng)用一樣打開(kāi)第三方應(yīng)用程序了:

    //打開(kāi)第三方應(yīng)用
    - (IBAction)thirdPartyApplicationClick:(UIButton *)sender { 
     NSString *url=@"cmj://myparams"; 
     [self openUrl:url];
    }
    //就像調(diào)用系統(tǒng)應(yīng)用一樣,協(xié)議后面可以傳遞一些參數(shù)(例如上面?zhèn)鬟f的myparams),這樣一來(lái)在應(yīng)用中可以在AppDelegate的
    -(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
    //代理方法中接收參數(shù)并解析。
     -(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{
         NSString *str=[NSString stringWithFormat:@"url:%@,source application:%@,params:%@",url,sourceApplication,[url host]]; 
         NSLog(@"%@",str);
         return YES;//是否打開(kāi)
     }
    
  2. 系統(tǒng)服務(wù)之短信與郵件
    調(diào)用系統(tǒng)內(nèi)置的應(yīng)用來(lái)發(fā)送短信、郵件相當(dāng)簡(jiǎn)單,但是這么操作也存在著一些弊端:當(dāng)你點(diǎn)擊了發(fā)送短信(或郵件)操作之后直接啟動(dòng)了系統(tǒng)的短信(或郵件)應(yīng)用程序,我們的應(yīng)用其實(shí)此時(shí)已經(jīng)處于一種掛起狀態(tài),發(fā)送完(短信或郵件)之后無(wú)法自動(dòng)回到應(yīng)用界面。
    如果想要在應(yīng)用程序內(nèi)部完成這些操作則可以利用iOS中的MessageUI.framework,它提供了關(guān)于短信和郵件的UI接口供開(kāi)發(fā)者在應(yīng)用程序內(nèi)部調(diào)用。從框架名稱不難看出這是一套UI接口,提供有現(xiàn)成的短信和郵件的編輯界面,開(kāi)發(fā)人員只需要通過(guò)編程的方式給短信和郵件控制器設(shè)置對(duì)應(yīng)的參數(shù)即可。

在MessageUI.framework中主要有兩個(gè)控制器類分別用于
發(fā)送短信(MFMessageComposeViewController)
郵件(MFMailComposeViewController),
它們均繼承于UINavigationController。由于兩個(gè)類使用方法十分類似,這里主要介紹一下MFMessageComposeViewController使用步驟:
*1. 創(chuàng)建MFMessageComposeViewController對(duì)象。
*2. 設(shè)置收件人recipients、信息正文body,如果運(yùn)行商支持主題和附件的話可以設(shè)置主題subject、附件attachments(可以通過(guò)canSendSubject、canSendAttachments方法判斷是否支持)
*3. 設(shè)置代理messageComposeDelegate(注意這里不是delegate屬性,因?yàn)閐elegate屬性已經(jīng)留給UINavigationController,MFMessageComposeViewController沒(méi)有覆蓋此屬性而是重新定義了一個(gè)代理),實(shí)現(xiàn)代理方法獲得發(fā)送狀態(tài)。

下面自定義一個(gè)發(fā)送短信的界面演示MFMessageComposeViewController的使用:
![](http://upload-images.jianshu.io/upload_images/1663804-91ac5967b31defaa.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
用戶通過(guò)在此界面輸入短信信息點(diǎn)擊“發(fā)送信息”調(diào)用MFMessageComposeViewController界面來(lái)展示或進(jìn)一步編輯信息,點(diǎn)擊MFMessageComposeViewController中的“發(fā)送”來(lái)完成短信發(fā)送工作,當(dāng)然用戶也可能點(diǎn)擊“取消”按鈕回到前一個(gè)短信編輯頁(yè)面。
![](http://upload-images.jianshu.io/upload_images/1663804-3e8935df249cb1e3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
實(shí)現(xiàn)代碼:
#import "KCSendMessageViewController.h"
@interface KCSendMessageViewController ()
@property (weak, nonatomic) IBOutlet UITextField *receivers;
@property (weak, nonatomic) IBOutlet UITextField *body;
@property (weak, nonatomic) IBOutlet UITextField subject;
@property (weak, nonatomic) IBOutlet UITextField attachments;
@end@implementation KCSendMessageViewController
#pragma mark - 控制器視圖方法- (void)viewDidLoad { [super viewDidLoad];}#pragma mark - UI事件
- (IBAction)sendMessageClick:(UIButton )sender {
//如果能發(fā)送文本信息
if([MFMessageComposeViewController canSendText]){
MFMessageComposeViewController messageController=[[MFMessageComposeViewController alloc]init];
//收件人
messageController.recipients=[self.receivers.text componentsSeparatedByString:@","];
//信息正文
messageController.body=self.body.text;
//設(shè)置代理,注意這里不是delegate而是messageComposeDelegate
messageController.messageComposeDelegate=self;
//如果運(yùn)行商支持主題
if([MFMessageComposeViewController canSendSubject]){
messageController.subject=self.subject.text;
}
//如果運(yùn)行商支持附件
if ([MFMessageComposeViewController canSendAttachments]) {
/
第一種方法
/ //messageController.attachments=...;
/
第二種方法
/
NSArray *attachments= [self.attachments.text componentsSeparatedByString:@","];
if (attachments.count>0) {
[attachments enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString path=[[NSBundle mainBundle]pathForResource:obj ofType:nil];
NSURL url=[NSURL fileURLWithPath:path]; [messageController addAttachmentURL:url withAlternateFilename:obj];
}];
}
/
第三種方法
/
NSString *path=[[NSBundle mainBundle]pathForResource:@"photo.jpg" ofType:nil];
NSURL url=[NSURL fileURLWithPath:path];
NSData data=[NSData dataWithContentsOfURL:url];
//
* attatchData:文件數(shù)據(jù) * uti:統(tǒng)一類型標(biāo)識(shí),標(biāo)識(shí)具體文件類型,詳情查看:幫助文檔中System-Declared Uniform Type Identifiers * fileName:展現(xiàn)給用戶看的文件名稱 */
[messageController addAttachmentData:data typeIdentifier:@"public.image" filename:@"photo.jpg"];
}
[self presentViewController:messageController animated:YES completion:nil];
}}
#pragma mark - MFMessageComposeViewController代理方法
//發(fā)送完成,不管成功與否
-(void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result{
switch (result) {
case MessageComposeResultSent: NSLog(@"發(fā)送成功.");
break;
case MessageComposeResultCancelled: NSLog(@"取消發(fā)送.");
break; default: NSLog(@"發(fā)送失敗.");
break; }
[self dismissViewControllerAnimated:YES completion:nil];
}
@end

這里需要強(qiáng)調(diào)一下:
.1) MFMessageComposeViewController的代理不是通過(guò)delegate屬性指定的而是通過(guò)messageComposeDelegate指定的。
.2) 可以通過(guò)幾種方式來(lái)指定發(fā)送的附件,在這個(gè)過(guò)程中請(qǐng)務(wù)必指定文件的后綴,否則在發(fā)送后無(wú)法正確識(shí)別文件類別(例如如果發(fā)送的是一張jpg圖片,在發(fā)送后無(wú)法正確查看圖片)。
.3) 無(wú)論發(fā)送成功與否代理方法
-(void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result都會(huì)執(zhí)行,通過(guò)代理參數(shù)中的result來(lái)獲得發(fā)送狀態(tài)。
其實(shí)只要熟悉了MFMessageComposeViewController之后,那么用于發(fā)送郵件的MFMailComposeViewController用法和步驟完全一致,只是功能不同。下面看一下MFMailComposeViewController的使用:
#import "KCSendEmailViewController.h"
@interface KCSendEmailViewController ()
@property (weak, nonatomic) IBOutlet UITextField *toTecipients;
//收件人
@property (weak, nonatomic) IBOutlet UITextField *ccRecipients;
//抄送人
@property (weak, nonatomic) IBOutlet UITextField *bccRecipients;
//密送人
@property (weak, nonatomic) IBOutlet UITextField *subject;
//主題
@property (weak, nonatomic) IBOutlet UITextField *body;
//正文
@property (weak, nonatomic) IBOutlet UITextField *attachments;
//附件
@end
@implementation KCSendEmailViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
#pragma mark - UI事件
- (IBAction)sendEmailClick:(UIButton *)sender {
//判斷當(dāng)前是否能夠發(fā)送郵件
if ([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController *mailController=[[MFMailComposeViewController alloc]init];
//設(shè)置代理,注意這里不是delegate,而是mailComposeDelegate
mailController.mailComposeDelegate=self;
//設(shè)置收件人
[mailController setToRecipients:[self.toTecipients.text componentsSeparatedByString:@","]];
//設(shè)置抄送人
if (self.ccRecipients.text.length>0) {
[mailController setCcRecipients:[self.ccRecipients.text componentsSeparatedByString:@","]];
}
//設(shè)置密送人
if (self.bccRecipients.text.length>0) {
[mailController setBccRecipients:[self.bccRecipients.text componentsSeparatedByString:@","]];
}
//設(shè)置主題
[mailController setSubject:self.subject.text];
//設(shè)置內(nèi)容
[mailController setMessageBody:self.body.text isHTML:YES];
//添加附件
if (self.attachments.text.length>0) {
NSArray *attachments=[self.attachments.text componentsSeparatedByString:@","] ;
[attachments enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSString *file=[[NSBundle mainBundle] pathForResource:obj ofType:nil];
NSData *data=[NSData dataWithContentsOfFile:file];
[mailController addAttachmentData:data mimeType:@"image/jpeg" fileName:obj];
//第二個(gè)參數(shù)是mimeType類型,jpg圖片對(duì)應(yīng)image/jpeg
}];
}
[self presentViewController:mailController animated:YES completion:nil];
}}
#pragma mark - MFMailComposeViewController代理方法
-(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error{
switch (result) {
case MFMailComposeResultSent:
NSLog(@"發(fā)送成功.");
break;
case MFMailComposeResultSaved://如果存儲(chǔ)為草稿(點(diǎn)取消會(huì)提示是否存儲(chǔ)為草稿,存儲(chǔ)后可以到系統(tǒng)郵件應(yīng)用的對(duì)應(yīng)草稿箱找到)
NSLog(@"郵件已保存.");
break;
case MFMailComposeResultCancelled:
NSLog(@"取消發(fā)送.");
break;
default:
NSLog(@"發(fā)送失敗.");
break;
}
if (error) {
NSLog(@"發(fā)送郵件過(guò)程中發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@",error.localizedDescription);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
@end

運(yùn)行效果:

圖片加載中...

![](http://upload-images.jianshu.io/upload_images/1663804-b9ba36d0596a4f8b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
2.2. 通訊錄
iOS中帶有一個(gè)Contacts應(yīng)用程序來(lái)管理聯(lián)系人,但是有些時(shí)候我們希望自己的應(yīng)用能夠訪問(wèn)或者修改這些信息,這個(gè)時(shí)候就要用到AddressBook.framework框架。
iOS中的通訊錄是存儲(chǔ)在數(shù)據(jù)庫(kù)中的,由于iOS的權(quán)限設(shè)計(jì),開(kāi)發(fā)人員是不允許直接訪問(wèn)通訊錄數(shù)據(jù)庫(kù)的,必須依靠AddressBook提供的標(biāo)準(zhǔn)API來(lái)實(shí)現(xiàn)通訊錄操作。通過(guò)AddressBook.framework開(kāi)發(fā)者可以從底層去操作AddressBook.framework的所有信息,但是需要注意的是這個(gè)框架是基于C語(yǔ)言編寫(xiě)的,無(wú)法使用ARC來(lái)管理內(nèi)存,開(kāi)發(fā)者需要自己管理內(nèi)存。下面大致介紹一下通訊錄操作中常用的類型:

.1) ABAddressBookRef:代表通訊錄對(duì)象,通過(guò)該對(duì)象開(kāi)發(fā)人員不用過(guò)多的關(guān)注通訊錄的存儲(chǔ)方式,可以直接以透明的方式去訪問(wèn)、保存(在使用AddressBook.framework操作聯(lián)系人時(shí),所有的增加、刪除、修改后都必須執(zhí)行保存操作,類似于Core Data)等。

.2) ABRecordRef:代表一個(gè)通用的記錄對(duì)象,可以是一條聯(lián)系人信息,也可以是一個(gè)群組,可以通過(guò)ABRecordGetRecordType()函數(shù)獲得具體類型。如果作為聯(lián)系人(事實(shí)上也經(jīng)常使用它作為聯(lián)系人),那么這個(gè)記錄記錄了一個(gè)完整的聯(lián)系人信息(姓名、性別、電話、郵件等),每條記錄都有一個(gè)唯一的ID標(biāo)示這條記錄(可以通過(guò)ABRecordGetRecordID()函數(shù)獲得)。

.3) ABPersonRef:代表聯(lián)系人信息,很少直接使用,實(shí)際開(kāi)發(fā)過(guò)程中通常會(huì)使用類型為“kABPersonType”的ABRecordRef來(lái)表示聯(lián)系人(由此可見(jiàn)ABPersonRef其實(shí)是一種類型為“kABPersonType”的ABRecordRef)

.4)ABGroupRef:代表群組,與ABPersonRef類似,很少直接使用ABGroupRef,而是使用類型為“kABGroupType”的ABRecordRef來(lái)表示群組,一個(gè)群組可以包含多個(gè)聯(lián)系人,一個(gè)聯(lián)系人也同樣可以多個(gè)群組。由于通訊錄操作的關(guān)鍵是對(duì)ABRecordRef的操作,首先看一下常用的操作通訊錄記錄的方法:
ABPersonCreate():創(chuàng)建一個(gè)類型為“kABPersonType”的ABRecordRef。ABRecordCopyValue():取得指定屬性的值。

ABRecordCopyCompositeName():取得聯(lián)系人(或群組)的復(fù)合信息(對(duì)于聯(lián)系人則包括:姓、名、公司等信息,對(duì)于群組則返回組名稱)。
ABRecordSetValue():設(shè)置ABRecordRef的屬性值。注意在設(shè)置ABRecordRef的值時(shí)又分為單值屬性和多值屬性:?jiǎn)沃祵傩栽O(shè)置只要通過(guò)ABRecordSetValue()方法指定屬性名和值即可;多值屬性則要先通過(guò)創(chuàng)建一個(gè)ABMutableMultiValueRef類型的變量,然后通過(guò)ABMultiValueAddValueAndLabel()方法依次添加屬性值,最后通過(guò)ABRecordSetValue()方法將ABMutableMultiValueRef類型的變量設(shè)置為記錄值。
ABRecordRemoveValue():刪除指定的屬性值。
注意:由于聯(lián)系人訪問(wèn)時(shí)(讀取、設(shè)置、刪除時(shí))牽扯到大量聯(lián)系人屬性,可以到ABPerson.h中查詢或者直接到幫助文檔“Personal Information Properties”

通訊錄的訪問(wèn)步驟一般如下:
調(diào)用ABAddressBookCreateWithOptions()方法創(chuàng)建通訊錄對(duì)象ABAddressBookRef。
調(diào)用ABAddressBookRequestAccessWithCompletion()方法獲得用戶授權(quán)訪問(wèn)通訊錄。
調(diào)用ABAddressBookCopyArrayOfAllPeople()、ABAddressBookCopyPeopleWithName()方法查詢聯(lián)系人信息。
調(diào)用ABAddressBookCopyArrayOfAllPeople()、ABAddressBookCopyPeopleWithName()方法查詢聯(lián)系人信息。
也就是說(shuō)如果要修改或者刪除都需要首先查詢對(duì)應(yīng)的聯(lián)系人,然后修改或刪除后提交更改。如果用戶要增加一個(gè)聯(lián)系人則不用進(jìn)行查詢,直接調(diào)用ABPersonCreate()方法創(chuàng)建一個(gè)ABRecord然后設(shè)置具體的屬性,調(diào)用ABAddressBookAddRecord方法添加即可下面就通過(guò)一個(gè)示例演示一下如何通過(guò)ABAddressBook.framework訪問(wèn)通訊錄,這個(gè)例子中通過(guò)一個(gè)UITableViewController模擬一下通訊錄的查看、刪除、添加操作。
主控制器視圖,用于顯示聯(lián)系人,修改刪除聯(lián)系人:

KCContactViewController.h
#import/** * 定義一個(gè)協(xié)議作為代理 */
@protocol KCContactDelegate
//新增或修改聯(lián)系人
-(void)editPersonWithFirstName:(NSString *)firstName lastName:(NSString *)lastName workNumber:(NSString *)workNumber;
//取消修改或新增
-(void)cancelEdit;@end@interface KCContactTableViewController : UITableViewController
@end

KCContactViewController.m
#import "KCContactTableViewController.h"
#import "KCAddPersonViewController.h"
@interface KCContactTableViewController ()
@property (assign,nonatomic) ABAddressBookRef addressBook;
//通訊錄
@property (strong,nonatomic) NSMutableArray *allPerson;
//通訊錄所有人員
@property (assign,nonatomic) int isModify;
//標(biāo)識(shí)是修改還是新增,通過(guò)選擇cell進(jìn)行導(dǎo)航則認(rèn)為是修改,否則視為新增
@property (assign,nonatomic) UITableViewCell *selectedCell;
//當(dāng)前選中的單元格
@end
@implementation KCContactTableViewController
#pragma mark - 控制器視圖
- (void)viewDidLoad {
     [super viewDidLoad]; 
     //請(qǐng)求訪問(wèn)通訊錄并初始化數(shù)據(jù)
     [self requestAddressBook];
}
//由于在整個(gè)視圖控制器周期內(nèi)addressBook都駐留在內(nèi)存中,所有當(dāng)控制器視圖銷毀時(shí)銷毀該對(duì)象
-(void)dealloc{ 
   if (self.addressBook!=NULL) { 
       CFRelease(self.addressBook); 
    }
}
#pragma mark - UI事件
//點(diǎn)擊刪除按鈕
- (IBAction)trashClick:(UIBarButtonItem *)sender {    
self.tableView.editing=!self.tableView.editing;
}
#pragma mark - UITableView數(shù)據(jù)源方法
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 
    return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 
    return self.allPerson.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    static NSString *identtityKey=@"myTableViewCellIdentityKey1"; 
    UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:identtityKey]; 
    if(cell==nil){ 
        cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identtityKey]; 
    }
    //取得一條人員記錄 
    ABRecordRef recordRef=(__bridge ABRecordRef)self.allPerson[indexPath.row]; 
   //取得記錄中得信息
    NSString *firstName=(__bridge NSString *) ABRecordCopyValue(recordRef, kABPersonFirstNameProperty);
   //注意這里進(jìn)行了強(qiáng)轉(zhuǎn),不用自己釋放資源 
    NSString *lastName=(__bridge NSString *)ABRecordCopyValue(recordRef, kABPersonLastNameProperty); 
    ABMultiValueRef phoneNumbersRef= ABRecordCopyValue(recordRef, kABPersonPhoneProperty);
   //獲取手機(jī)號(hào),注意手機(jī)號(hào)是ABMultiValueRef類,有可能有多條//
    NSArray *phoneNumbers=(__bridge NSArray *)ABMultiValueCopyArrayOfAllValues(phoneNumbersRef);
   //取得CFArraryRef類型的手機(jī)記錄并轉(zhuǎn)化為
   NSArrary long count= ABMultiValueGetCount(phoneNumbersRef);
 // 
     for(int i=0;i0) { 
         cell.detailTextLabel.text=(__bridge NSString *)(ABMultiValueCopyValueAtIndex(phoneNumbersRef, 0));
      }
    if(ABPersonHasImageData(recordRef)){
        //如果有照片數(shù)據(jù)
        NSData *imageData= (__bridge NSData *)(ABPersonCopyImageData(recordRef)); 
        cell.imageView.image=[UIImage imageWithData:imageData];
     }else{ 
         cell.imageView.image=[UIImage imageNamed:@"avatar"];
         //沒(méi)有圖片使用默認(rèn)頭像 
    }
    //使用cell的tag存儲(chǔ)記錄id 
    cell.tag=ABRecordGetRecordID(recordRef); 
   return cell;
 }
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { 
    if (editingStyle == UITableViewCellEditingStyleDelete) {     
        ABRecordRef recordRef=(__bridge ABRecordRef )self.allPerson[indexPath.row];
        [self removePersonWithRecord:recordRef];
        //從通訊錄刪除
        [self.allPerson removeObjectAtIndex:indexPath.row];
        //從數(shù)組移除
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
        //從列表刪除
     } else if (editingStyle == UITableViewCellEditingStyleInsert) {
      // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
     } 
}
#pragma mark - UITableView代理方法
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ 
    self.isModify=1; 
    self.selectedCell=[tableView cellForRowAtIndexPath:indexPath];
    [self performSegueWithIdentifier:@"AddPerson" sender:self];
}
 #pragma mark - Navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
   if([segue.identifier isEqualToString:@"AddPerson"]){ 
       UINavigationController *navigationController=(UINavigationController *)segue.destinationViewController; 
   //根據(jù)導(dǎo)航控制器取得添加/修改人員的控制器視圖 
   KCAddPersonViewController *addPersonController=(KCAddPersonViewController *)navigationController.topViewController; addPersonController.delegate=self; 
   //如果是通過(guò)選擇cell進(jìn)行的導(dǎo)航操作說(shuō)明是修改,否則為添加 
   if (self.isModify) { 
       UITableViewCell *cell=self.selectedCell; addPersonController.recordID=(ABRecordID)cell.tag;
       //設(shè)置 NSArray *array=[cell.textLabel.text componentsSeparatedByString:@" "];
       if (array.count>0) {
           addPersonController.firstNameText=[array firstObject]; 
       }
       if (array.count>1) {
           addPersonController.lastNameText=[array lastObject]; 
       } 
       addPersonController.workPhoneText=cell.detailTextLabel.text; 
    } 
}}
#pragma mark - KCContact代理方法
-(void)editPersonWithFirstName:(NSString *)firstName lastName:(NSString *)lastName workNumber:(NSString *)workNumber{ 
    if (self.isModify) { 
        UITableViewCell *cell=self.selectedCell; NSIndexPath *indexPath= [self.tableView indexPathForCell:cell]; 
        [self modifyPersonWithRecordID:(ABRecordID)cell.tag firstName:firstName lastName:lastName workNumber:workNumber];
        [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationRight]; 
    }else{
        [self addPersonWithFirstName:firstName lastName:lastName workNumber:workNumber];
        //通訊簿中添加信息 
        [self initAllPerson];
        //重新初始化數(shù)據(jù) 
        [self.tableView reloadData];
    } 
    self.isModify=0;
}
-(void)cancelEdit{ 
    self.isModify=0;
}
#pragma mark - 私有方法/** * 請(qǐng)求訪問(wèn)通訊錄 */
-(void)requestAddressBook{
    //創(chuàng)建通訊錄對(duì)象 
    self.addressBook=ABAddressBookCreateWithOptions(NULL, NULL); 
    //請(qǐng)求訪問(wèn)用戶通訊錄,注意無(wú)論成功與否block都會(huì)調(diào)用 
    ABAddressBookRequestAccessWithCompletion(self.addressBook, ^(bool granted, CFErrorRef error) {
       if (!granted) { NSLog(@"未獲得通訊錄訪問(wèn)權(quán)限!"); 
    }
    [self initAllPerson]; 
   });
}
/** * 取得所有通訊錄記錄 */
-(void)initAllPerson{ 
    //取得通訊錄訪問(wèn)授權(quán) 
    ABAuthorizationStatus authorization= ABAddressBookGetAuthorizationStatus(); 
    //如果未獲得授權(quán) 
    if (authorization!=kABAuthorizationStatusAuthorized) { 
        NSLog(@"尚未獲得通訊錄訪問(wèn)授權(quán)!"); 
        return ;
     } 
    //取得通訊錄中所有人員記錄
    CFArrayRef allPeople= ABAddressBookCopyArrayOfAllPeople(self.addressBook); 
    self.allPerson=(__bridge NSMutableArray *)allPeople;
    //釋放資源 CFRelease(allPeople);
}
/** * 刪除指定的記錄 * * @param recordRef 要?jiǎng)h除的記錄 */
-(void)removePersonWithRecord:(ABRecordRef)recordRef{ 
    ABAddressBookRemoveRecord(self.addressBook, recordRef, NULL);
    //刪除 
    ABAddressBookSave(self.addressBook, NULL);
    //刪除之后提交更改
}
/** * 根據(jù)姓名刪除記錄 */
-(void)removePersonWithName:(NSString *)personName{ 
    CFStringRef personNameRef=(__bridge CFStringRef)(personName); 
    CFArrayRef recordsRef= ABAddressBookCopyPeopleWithName(self.addressBook, personNameRef);
    //根據(jù)人員姓名查找 
    CFIndex count= CFArrayGetCount(recordsRef);
    //取得記錄數(shù) for (CFIndex i=0; i

新增或修改控制器視圖,用于顯示一個(gè)聯(lián)系人的信息或者新增一個(gè)聯(lián)系人:
KCAddPersonViewController.h

#import
@protocol KCContactDelegate;
@interface KCAddPersonViewController : UIViewController
@property (assign,nonatomic) int recordID;
//通訊錄記錄id,如果ID不為0則代表修改否則認(rèn)為是新增
@property (strong,nonatomic) NSString *firstNameText;
@property (strong,nonatomic) NSString *lastNameText;
@property (strong,nonatomic) NSString *workPhoneText;
@property (strong,nonatomic) iddelegate;@end

KCAddPersonViewController.m

#import "KCAddPersonViewController.h"
#import "KCContactTableViewController.h"
@interface KCAddPersonViewController ()
@property (weak, nonatomic) IBOutlet UITextField *firstName;
@property (weak, nonatomic) IBOutlet UITextField *lastName;
@property (weak, nonatomic) IBOutlet UITextField *workPhone;
@end
@implementation KCAddPersonViewController
- (void)viewDidLoad { 
    [super viewDidLoad]; 
    [self setupUI];
}
#pragma mark - UI事件
- (IBAction)cancelClick:(UIBarButtonItem *)sender { 
    [self.delegate cancelEdit]; 
    [self dismissViewControllerAnimated:YES completion:nil];
}
- (IBAction)doneClick:(UIBarButtonItem *)sender { 
    //調(diào)用代理方法 
    [self.delegate editPersonWithFirstName:self.firstName.text lastName:self.lastName.text workNumber:self.workPhone.text]; 
    [self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - 私有方法
-(void)setupUI{
     if (self.recordID) {
        //如果ID不為0則認(rèn)為是修改,此時(shí)需要初始化界面
        self.firstName.text=self.firstNameText; 
        self.lastName.text=self.lastNameText; 
        self.workPhone.text=self.workPhoneText; 
     }
}
@end

運(yùn)行效果:![](http://upload-images.jianshu.io/upload_images/1663804-aadc436a1d0ba28f.gif?imageMogr2/auto-orient/strip)

備注:
1.上文中所指的以Ref結(jié)尾的對(duì)象事實(shí)上是該對(duì)象的指針(或引用),在C語(yǔ)言的框架中多數(shù)類型會(huì)以Ref結(jié)尾,這個(gè)類型本身就是一個(gè)指針,定義時(shí)不需要加“*”。

2.通常方法中包含copy、create、new、retain等關(guān)鍵字的方法創(chuàng)建的變量使用之后需要調(diào)用對(duì)應(yīng)的release方法釋放。例如:使用ABPersonCreate();創(chuàng)建完ABRecordRef變量后使用CFRelease()方法釋放。

3.在與很多C語(yǔ)言框架交互時(shí)可以都存在Obj-C和C語(yǔ)言類型之間的轉(zhuǎn)化(特別是Obj-C和Core Foundation框架中的一些轉(zhuǎn)化),此時(shí)可能會(huì)用到橋接,只要在強(qiáng)轉(zhuǎn)之后前面加上”__bridge”即可,經(jīng)過(guò)橋接轉(zhuǎn)化后的類型不需要再去手動(dòng)維護(hù)內(nèi)存,也就不需要使用對(duì)應(yīng)的release方法釋放內(nèi)存。

4.AddressBook框架中很多類型的創(chuàng)建、屬性設(shè)置等都是以這個(gè)類型名開(kāi)發(fā)頭的方法來(lái)創(chuàng)建的,事實(shí)上如果大家熟悉了其他框架會(huì)發(fā)現(xiàn)也都是類似的,這是Apple開(kāi)發(fā)中約定俗成的命名規(guī)則(特別是C語(yǔ)言框架)。例如:要給ABRecordRef類型的變量設(shè)置屬性則可以通過(guò)ABRecordSetValue()方法完成。

AddressBookUI

使用AddressBook.framework來(lái)操作通訊錄特點(diǎn)就是可以對(duì)通訊錄有更加精確的控制,但是缺點(diǎn)就是面對(duì)大量C語(yǔ)言API稍嫌麻煩,于是Apple官方提供了另一套框架供開(kāi)發(fā)者使用,那就是AddressBookUI.framework。例如前面查看、新增、修改人員的界面這個(gè)框架就提供了現(xiàn)成的控制器視圖供開(kāi)發(fā)者使用。

下面是這個(gè)框架中提供的控制器視圖:

  1. ABPersonViewController:用于查看聯(lián)系人信息(可設(shè)置編輯)。需要設(shè)置displayedPerson屬性來(lái)設(shè)置要顯示或編輯的聯(lián)系人。
  2. ABNewPersonViewController:用于新增聯(lián)系人信息。
  3. ABUnknownPersonViewController:用于顯示一個(gè)未知聯(lián)系人(尚未保存的聯(lián)系人)信息。需要設(shè)置displayedPerson屬性來(lái)設(shè)置要顯示的未知聯(lián)系人。

以上三個(gè)控制器視圖均繼承于UIViewController,在使用過(guò)程中必須使用一個(gè)UINavigationController進(jìn)行包裝,否則只能看到視圖內(nèi)容無(wú)法進(jìn)行操作(例如對(duì)于ABNewPersonViewController如果不使用UINavigationController進(jìn)行包裝則沒(méi)有新增和取消按鈕),同時(shí)注意包裝后的控制器視圖不需要處理具體新增、修改邏輯(增加和修改的處理邏輯對(duì)應(yīng)的控制器視圖內(nèi)部已經(jīng)完成),但是必須處理控制器的關(guān)閉操作(調(diào)用dismissViewControllerAnimated::方法),并且可以通過(guò)代理方法獲得新增、修改的聯(lián)系人。下面看一下三個(gè)控制器視圖的代理方法:

ABPersonViewController的displayViewDelegate代理方法:

-(BOOL)personViewController:(ABPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:``(ABMultiValueIdentifier)identifier:
此方法會(huì)在選擇了一個(gè)聯(lián)系人屬性后觸發(fā),四個(gè)參數(shù)分別代表:使用的控制器視圖、所查看的聯(lián)系人、所選則的聯(lián)系人屬性、該屬性是否是多值屬性。

ABNewPersonViewController的newPersonViewDelegate代理方法:

-(void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(ABRecordRef)person:
 點(diǎn)擊取消或完成后觸發(fā),如果參數(shù)中的person為NULL說(shuō)明點(diǎn)擊了取消,否則說(shuō)明點(diǎn)擊了完成。無(wú)論是取消還是完成操作,此方法調(diào)用時(shí)保存操作已經(jīng)進(jìn)行完畢,不需要在此方法中自己保存聯(lián)系人信息。

ABUnkownPersonViewcontroller的unkownPersonViewDelegate代理方法:

-(void)unknownPersonViewController:(ABUnknownPersonViewController *)unknownCardViewController didResolveToPerson:(ABRecordRef)person:
保存此聯(lián)系人時(shí)調(diào)用,調(diào)用后將此聯(lián)系人返回。
-(BOOL)unknownPersonViewController:(ABUnknownPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier:
選擇一個(gè)位置聯(lián)系人屬性之后執(zhí)行,返回值代表是否執(zhí)行默認(rèn)的選擇操作(例如如果是手機(jī)號(hào),默認(rèn)操作會(huì)撥打此電話)

除了上面三類控制器視圖在AddressBookUI中還提供了另外一個(gè)控制器視圖ABPeoplePickerNavigationController,它與之前介紹的UIImagePickerController、MPMediaPickerController類似,只是他是用來(lái)選擇一個(gè)聯(lián)系人的。這個(gè)控制器視圖本身繼承于UINavigationController,視圖自身的“組”、“取消”按鈕操作不需要開(kāi)發(fā)者來(lái)完成(例如開(kāi)發(fā)者不用在點(diǎn)擊取消是關(guān)閉當(dāng)前控制器視圖,它自身已經(jīng)實(shí)現(xiàn)了關(guān)閉方法),當(dāng)然這里主要說(shuō)一下這個(gè)

控制器視圖的peoplePickerDelegate代理方法:

-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person:
選擇一個(gè)聯(lián)系人后執(zhí)行。此代理方法實(shí)現(xiàn)后代理方法“
-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier”
不會(huì)再執(zhí)行。并且一旦實(shí)現(xiàn)了這個(gè)代理方法用戶只能選擇到聯(lián)系人視圖,無(wú)法查看具體聯(lián)系人的信息。
-(void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker:
用戶點(diǎn)擊取消后執(zhí)行。
-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier:
選擇聯(lián)系人具體的屬性后執(zhí)行,注意如果要執(zhí)行此方法則不能實(shí)現(xiàn)
-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person
代理方法,此時(shí)如果點(diǎn)擊一個(gè)具體聯(lián)系人會(huì)導(dǎo)航到聯(lián)系人詳細(xì)信息界面,用戶點(diǎn)擊具體的屬性后觸發(fā)此方法。

下面就看一下上面四個(gè)控制器視圖的使用方法,在下面的程序中定義了四個(gè)按鈕,點(diǎn)擊不同的按鈕調(diào)用不同的控制器視圖用于演示:

#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
     [super viewDidLoad];
}
#pragma mark - UI事件//添加聯(lián)系人
- (IBAction)addPersonClick:(UIButton *)sender { 
    ABNewPersonViewController *newPersonController=[[ABNewPersonViewController alloc]init]; 
    //設(shè)置代理 
    newPersonController.newPersonViewDelegate=self; 
    //注意ABNewPersonViewController必須包裝一層UINavigationController才能使用,否則不會(huì)出現(xiàn)取消和完成按鈕,無(wú)法進(jìn)行保存等操作 
    UINavigationController *navigationController=[[UINavigationController alloc]initWithRootViewController:newPersonController]; 
    [self presentViewController:navigationController animated:YES completion:nil];
}
- (IBAction)unknownPersonClick:(UIButton *)sender { 
    ABUnknownPersonViewController *unknownPersonController=[[ABUnknownPersonViewController alloc]init]; 
    //設(shè)置未知人員 
    ABRecordRef recordRef=ABPersonCreate(); 
    ABRecordSetValue(recordRef, kABPersonFirstNameProperty, @"Kenshin", NULL); 
    ABRecordSetValue(recordRef, kABPersonLastNameProperty, @"Cui", NULL);
    ABMultiValueRef multiValueRef=ABMultiValueCreateMutable(kABStringPropertyType); 
    ABMultiValueAddValueAndLabel(multiValueRef, @"18500138888", kABHomeLabel, NULL); 
    ABRecordSetValue(recordRef, kABPersonPhoneProperty, multiValueRef, NULL); unknownPersonController.displayedPerson=recordRef; 
    //設(shè)置代理 
    unknownPersonController.unknownPersonViewDelegate=self;
    //設(shè)置其他屬性 
    unknownPersonController.allowsActions=YES;
   //顯示標(biāo)準(zhǔn)操作按鈕 
    unknownPersonController.allowsAddingToAddressBook=YES;
    //是否允許將聯(lián)系人添加到地址簿 
    CFRelease(multiValueRef); 
    CFRelease(recordRef); 
    //使用導(dǎo)航控制器包裝 
    UINavigationController *navigationController=[[UINavigationController alloc]initWithRootViewController:unknownPersonController]; 
    [self presentViewController:navigationController animated:YES completion:nil];
}
- (IBAction)showPersonClick:(UIButton *)sender { 
    ABPersonViewController *personController=[[ABPersonViewController alloc]init]; 
    //設(shè)置聯(lián)系人 ABAddressBookRef addressBook=ABAddressBookCreateWithOptions(NULL, NULL); 
    ABRecordRef recordRef= ABAddressBookGetPersonWithRecordID(addressBook, 1);
    //取得id為1的聯(lián)系人記錄 
    personController.displayedPerson=recordRef; 
    //設(shè)置代理 
    personController.personViewDelegate=self; 
    //設(shè)置其他屬性 
    personController.allowsActions=YES;
    //是否顯示發(fā)送信息、共享聯(lián)系人等按鈕 
    personController.allowsEditing=YES;
    //允許編輯// 
    personController.displayedProperties=@[@(kABPersonFirstNameProperty),@(kABPersonLastNameProperty)];
    //顯示的聯(lián)系人屬性信息,默認(rèn)顯示所有信息 //使用導(dǎo)航控制器包裝
    UINavigationController *navigationController=[[UINavigationController alloc]initWithRootViewController:personController]; 
    [self presentViewController:navigationController animated:YES completion:nil];
}
- (IBAction)selectPersonClick:(UIButton *)sender { 
    ABPeoplePickerNavigationController *peoplePickerController=[[ABPeoplePickerNavigationController alloc]init]; 
    //設(shè)置代理 
    peoplePickerController.peoplePickerDelegate=self;
    [self presentViewController:peoplePickerController animated:YES completion:nil];
}
#pragma mark - ABNewPersonViewController代理方法//完成新增(點(diǎn)擊取消和完成按鈕時(shí)調(diào)用),注意這里不用做實(shí)際的通訊錄增加工作,此代理方法調(diào)用時(shí)已經(jīng)完成新增,當(dāng)保存成功的時(shí)候參數(shù)中得person會(huì)返回保存的記錄,如果點(diǎn)擊取消person為NULL
-(void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(ABRecordRef)person{ 
    //如果有聯(lián)系人信息 
    if (person) { 
        NSLog(@"%@ 信息保存成功.",(__bridge NSString *)(ABRecordCopyCompositeName(person))); 
    }else{ 
        NSLog(@"點(diǎn)擊了取消."); 
    }
    //關(guān)閉模態(tài)視圖窗口 
    [self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - ABUnknownPersonViewController代理方法//保存未知聯(lián)系人時(shí)觸發(fā)
-(void)unknownPersonViewController:(ABUnknownPersonViewController *)unknownCardViewController didResolveToPerson:(ABRecordRef)person{
     if (person) { 
        NSLog(@"%@ 信息保存成功!",(__bridge NSString *)(ABRecordCopyCompositeName(person)));
     } 
    [self dismissViewControllerAnimated:YES completion:nil];
}
//選擇一個(gè)人員屬性后觸發(fā),返回值YES表示觸發(fā)默認(rèn)行為操作,否則執(zhí)行代理中自定義的操作
-(BOOL)unknownPersonViewController:(ABUnknownPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
     if (person) {
         NSLog(@"選擇了屬性:%i,值:%@.",property,(__bridge NSString *)ABRecordCopyValue(person, property)); 
     }
 return NO;
}
#pragma mark - ABPersonViewController代理方法
//選擇一個(gè)人員屬性后觸發(fā),返回值YES表示觸發(fā)默認(rèn)行為操作,否則執(zhí)行代理中自定義的操作
-(BOOL)personViewController:(ABPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
    if (person) { 
        NSLog(@"選擇了屬性:%i,值:%@.",property,(__bridge NSString *)ABRecordCopyValue(person, property)); 
     } 
return NO;
}
#pragma mark - ABPeoplePickerNavigationController代理方法//選擇一個(gè)聯(lián)系人后,注意這個(gè)代理方法實(shí)現(xiàn)后屬性選擇的方法將不會(huì)再調(diào)用
-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person{ 
    if (person) {
        NSLog(@"選擇了%@.",(__bridge NSString *)(ABRecordCopyCompositeName(person)));
    }
}
//選擇屬性之后,注意如果上面的代理方法實(shí)現(xiàn)后此方法不會(huì)被調(diào)用//
-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
    // if (person && property) {
       // NSLog(@"選擇了屬性:%i,值:%@.",property,(__bridge NSString *)ABRecordCopyValue(person, property));
    // }
}
//點(diǎn)擊取消按鈕    
-(void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{
     NSLog(@"取消選擇.");
}
@end

![](http://upload-images.jianshu.io/upload_images/1663804-52aeaf5b8ee5d9ac.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![](http://upload-images.jianshu.io/upload_images/1663804-d9e2e1b7c8f89c24.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![](http://upload-images.jianshu.io/upload_images/1663804-ea2b1abc409270b1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![](http://upload-images.jianshu.io/upload_images/1663804-7bb9e96ad7f56df6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

注意:為了讓大家可以更加清楚的看到幾個(gè)控制器視圖的使用,這里并沒(méi)有結(jié)合前面的UITableViewController來(lái)使用,事實(shí)上大家結(jié)合前面UITableViewController可以做一個(gè)完善的通訊錄應(yīng)用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,673評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 178,610評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,939評(píng)論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,668評(píng)論 6 412
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 56,004評(píng)論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評(píng)論 3 449
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 43,173評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,705評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,426評(píng)論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,656評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評(píng)論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,833評(píng)論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,247評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,580評(píng)論 1 295
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,371評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,621評(píng)論 2 380

推薦閱讀更多精彩內(nèi)容

  • *面試心聲:其實(shí)這些題本人都沒(méi)怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來(lái)就是把...
    Dove_iOS閱讀 27,198評(píng)論 30 471
  • 一、系統(tǒng)應(yīng)用 在開(kāi)發(fā)某些應(yīng)用時(shí),我們可能希望能夠調(diào)用iOS系統(tǒng)內(nèi)置的電話、短信、郵件、瀏覽器應(yīng)用,或者直接調(diào)用安裝...
    執(zhí)著丶執(zhí)念閱讀 1,673評(píng)論 8 25
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,829評(píng)論 18 139
  • 中國(guó)股市政策的制定,是以鞏固和加強(qiáng)黨的領(lǐng)導(dǎo)為出發(fā)點(diǎn)的,這是股市政策風(fēng)險(xiǎn)的根源。保持政權(quán)的穩(wěn)定,是黨和政府的“大局”...
    會(huì)逃跑的小偷閱讀 76評(píng)論 0 0
  • 1:準(zhǔn)備工作 在網(wǎng)頁(yè)http://dev.mysql.com/downloads/mysql/下載Windows ...
    clshinem閱讀 1,052評(píng)論 1 1