前言
今天給大家詳細(xì)介紹一下iOS之短信,郵件,通信錄技術(shù)的具體實(shí)現(xiàn)
-
系統(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://”其中“//”可以省略寫成“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) }
系統(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ǔ)言編寫的,無(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è)框架中提供的控制器視圖:
- ABPersonViewController:用于查看聯(lián)系人信息(可設(shè)置編輯)咐汞。需要設(shè)置displayedPerson屬性來(lái)設(shè)置要顯示或編輯的聯(lián)系人。
- ABNewPersonViewController:用于新增聯(lián)系人信息儒鹿。
- 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
注意:為了讓大家可以更加清楚的看到幾個(gè)控制器視圖的使用晌纫,這里并沒(méi)有結(jié)合前面的UITableViewController來(lái)使用税迷,事實(shí)上大家結(jié)合前面UITableViewController可以做一個(gè)完善的通訊錄應(yīng)用。