今天給大家詳細介紹一下iOS之短信,郵件,通信錄技術的具體實現
在開發某些應用時可能希望能夠調用iOS系統內置的電話、短信、郵件、浏覽器應用,此時你可以直接使用UIApplication的OpenURL:方法指定特定的協議來打開不同的系統應用。常用的協議如下:
打電話:tel:或者tel://、telprompt:或telprompt://(撥打電話前有提示)
發短信:sms:或者sms://
發送郵件:mailto:或者mailto://
啟動浏覽器:http:或者http://
下面以一個簡單的demo演示如何調用上面幾種系統應用:
#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];//這種方式會直接撥打電話
NSString *url=[NSString stringWithFormat:@"telprompt://%@",phoneNumber];//這種方式會提示用戶確認是否撥打電話
[self openUrl:url];
}
//發送短信
- (IBAction)sendMessageClick:(UIButton *)sender {
NSString *phoneNumber=@"18500138888";
NSString *url=[NSString stringWithFormat:@"sms://%@",phoneNumber];
[self openUrl:url];
}
//發送郵件
- (IBAction)sendEmailClick:(UIButton *)sender {
NSString *mailAddress=@"[email protected]";
NSString *url=[NSString stringWithFormat:@"mailto://%@",mailAddress];
[self openUrl:url];
}
//浏覽網頁
- (IBAction)browserClick:(UIButton *)sender {
NSString *url=@"http://www.cnblogs.com/kenshincui";
[self openUrl:url];
}
#pragma mark - 私有方法
-(void)openUrl:(NSString *)urlStr{
//注意url中包含協議名稱,iOS根據協議確定調用哪個應用,例如發送郵件是“sms://”其中“//”可以省略寫成“sms:”(其他協議也是如此)
NSURL *url=[NSURL URLWithString:urlStr];
UIApplication *application=[UIApplication sharedApplication];
if(![application canOpenURL:url]){
NSLog(@"無法打開\"%@\",請確保此應用已經正確安裝.",url);
return;
}
[[UIApplication sharedApplication] openURL:url];
}
@end
不難發現當openURL:方法只要指定一個URL Schame並且已經安裝了對應的應用程序就可以打開此應用。當然,如果是自己開發的應用也可以調用openURL方法來打開。假設你現在開發了一個應用A,如果用戶機器上已經安裝了此應用,並且在應用B中希望能夠直接打開A。那麼首先需要確保應用A已經配置了Url Types,具體方法就是在plist文件中添加URL types節點並配置URL Schemas作為具體協議,配置URL identifier作為這個URL的唯一標識,如下圖
然後就可以調用openURL方法像打開系統應用一樣打開第三方應用程序了:
//打開第三方應用
- (IBAction)thirdPartyApplicationClick:(UIButton *)sender {
NSString *url=@"cmj://myparams";
[self openUrl:url];
}
就像調用系統應用一樣,協議後面可以傳遞一些參數(例如上面傳遞的myparams),這樣一來在應用中可以在AppDelegate的-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation代理方法中接收參數並解析。
-(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;//是否打開
}
調用系統內置的應用來發送短信、郵件相當簡單,但是這麼操作也存在著一些弊端:當你點擊了發送短信(或郵件)操作之後直接啟動了系統的短信(或郵件)應用程序,我們的應用其實此時已經處於一種掛起狀態,發送完(短信或郵件)之後無法自動回到應用界面。如果想要在應用程序內部完成這些操作則可以利用iOS中的MessageUI.framework,它提供了關於短信和郵件的UI接口供開發者在應用程序內部調用。從框架名稱不難看出這是一套UI接口,提供有現成的短信和郵件的編輯界面,開發人員只需要通過編程的方式給短信和郵件控制器設置對應的參數即可。
在MessageUI.framework中主要有兩個控制器類分別用於發送短信(MFMessageComposeViewController)和郵件(MFMailComposeViewController),它們均繼承於UINavigationController。由於兩個類使用方法十分類似,這裡主要介紹一下MFMessageComposeViewController使用步驟:
創建MFMessageComposeViewController對象。
設置收件人recipients、信息正文body,如果運行商支持主題和附件的話可以設置主題subject、附件attachments(可以通過canSendSubject、canSendAttachments方法判斷是否支持)
設置代理messageComposeDelegate(注意這裡不是delegate屬性,因為delegate屬性已經留給UINavigationController,MFMessageComposeViewController沒有覆蓋此屬性而是重新定義了一個代理),實現代理方法獲得發送狀態。
下面自定義一個發送短信的界面演示MFMessageComposeViewController的使用:
vcew0ru49rbM0MWx4Lyt0rPD5qGjPGJyIC8+DQo8aW1nIGFsdD0="這裡寫圖片描述" src="/uploadfile/Collfiles/20160502/201605021120083.png" title="\" />
實現代碼:
//
// KCSendMessageViewController.m
// iOSSystemApplication
//
// Created by Kenshin Cui on 14/04/05.
// Copyright (c) 2014年 cmjstudio. All rights reserved.
//
#import "KCSendMessageViewController.h"
#import
@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 {
//如果能發送文本信息
if([MFMessageComposeViewController canSendText]){
MFMessageComposeViewController *messageController=[[MFMessageComposeViewController alloc]init];
//收件人
messageController.recipients=[self.receivers.text componentsSeparatedByString:@","];
//信息正文
messageController.body=self.body.text;
//設置代理,注意這裡不是delegate而是messageComposeDelegate
messageController.messageComposeDelegate=self;
//如果運行商支持主題
if([MFMessageComposeViewController canSendSubject]){
messageController.subject=self.subject.text;
}
//如果運行商支持附件
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:文件數據
* uti:統一類型標識,標識具體文件類型,詳情查看:幫助文檔中System-Declared Uniform Type Identifiers
* fileName:展現給用戶看的文件名稱
*/
// [messageController addAttachmentData:data typeIdentifier:@"public.image" filename:@"photo.jpg"];
}
[self presentViewController:messageController animated:YES completion:nil];
}
}
#pragma mark - MFMessageComposeViewController代理方法
//發送完成,不管成功與否
-(void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result{
switch (result) {
case MessageComposeResultSent:
NSLog(@"發送成功.");
break;
case MessageComposeResultCancelled:
NSLog(@"取消發送.");
break;
default:
NSLog(@"發送失敗.");
break;
}
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
這裡需要強調一下:
.1) MFMessageComposeViewController的代理不是通過delegate屬性指定的而是通過messageComposeDelegate指定的。
.2) 可以通過幾種方式來指定發送的附件,在這個過程中請務必指定文件的後綴,否則在發送後無法正確識別文件類別(例如如果發送的是一張jpg圖片,在發送後無法正確查看圖片)。
.3) 無論發送成功與否代理方法-(void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result都會執行,通過代理參數中的result來獲得發送狀態。
其實只要熟悉了MFMessageComposeViewController之後,那麼用於發送郵件的MFMailComposeViewController用法和步驟完全一致,只是功能不同。下面看一下MFMailComposeViewController的使用:
#import "KCSendEmailViewController.h"
#import
@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 {
//判斷當前是否能夠發送郵件
if ([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController *mailController=[[MFMailComposeViewController alloc]init];
//設置代理,注意這裡不是delegate,而是mailComposeDelegate
mailController.mailComposeDelegate=self;
//設置收件人
[mailController setToRecipients:[self.toTecipients.text componentsSeparatedByString:@","]];
//設置抄送人
if (self.ccRecipients.text.length>0) {
[mailController setCcRecipients:[self.ccRecipients.text componentsSeparatedByString:@","]];
}
//設置密送人
if (self.bccRecipients.text.length>0) {
[mailController setBccRecipients:[self.bccRecipients.text componentsSeparatedByString:@","]];
}
//設置主題
[mailController setSubject:self.subject.text];
//設置內容
[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];//第二個參數是mimeType類型,jpg圖片對應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(@"發送成功.");
break;
case MFMailComposeResultSaved://如果存儲為草稿(點取消會提示是否存儲為草稿,存儲後可以到系統郵件應用的對應草稿箱找到)
NSLog(@"郵件已保存.");
break;
case MFMailComposeResultCancelled:
NSLog(@"取消發送.");
break;
default:
NSLog(@"發送失敗.");
break;
}
if (error) {
NSLog(@"發送郵件過程中發生錯誤,錯誤信息:%@",error.localizedDescription);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
運行效果:
iOS中帶有一個Contacts應用程序來管理聯系人,但是有些時候我們希望自己的應用能夠訪問或者修改這些信息,這個時候就要用到AddressBook.framework框架。iOS中的通訊錄是存儲在數據庫中的,由於iOS的權限設計,開發人員是不允許直接訪問通訊錄數據庫的,必須依靠AddressBook提供的標准API來實現通訊錄操作。通過AddressBook.framework開發者可以從底層去操作AddressBook.framework的所有信息,但是需要注意的是這個框架是基於C語言編寫的,無法使用ARC來管理內存,開發者需要自己管理內存。下面大致介紹一下通訊錄操作中常用的類型:
.1) ABAddressBookRef:代表通訊錄對象,通過該對象開發人員不用過多的關注通訊錄的存儲方式,可以直接以透明的方式去訪問、保存(在使用AddressBook.framework操作聯系人時,所有的增加、刪除、修改後都必須執行保存操作,類似於Core Data)等。
.2) ABRecordRef:代表一個通用的記錄對象,可以是一條聯系人信息,也可以是一個群組,可以通過ABRecordGetRecordType()函數獲得具體類型。如果作為聯系人(事實上也經常使用它作為聯系人),那麼這個記錄記錄了一個完整的聯系人信息(姓名、性別、電話、郵件等),每條記錄都有一個唯一的ID標示這條記錄(可以通過ABRecordGetRecordID()函數獲得)。
.3) ABPersonRef:代表聯系人信息,很少直接使用,實際開發過程中通常會使用類型為“kABPersonType”的ABRecordRef來表示聯系人(由此可見ABPersonRef其實是一種類型為“kABPersonType”的ABRecordRef)
.4)ABGroupRef:代表群組,與ABPersonRef類似,很少直接使用ABGroupRef,而是使用類型為“kABGroupType”的ABRecordRef來表示群組,一個群組可以包含多個聯系人,一個聯系人也同樣可以多個群組。
由於通訊錄操作的關鍵是對ABRecordRef的操作,首先看一下常用的操作通訊錄記錄的方法:
ABPersonCreate():創建一個類型為“kABPersonType”的ABRecordRef。
ABRecordCopyValue():取得指定屬性的值。
ABRecordCopyCompositeName():取得聯系人(或群組)的復合信息(對於聯系人則包括:姓、名、公司等信息,對於群組則返回組名稱)。
ABRecordSetValue():設置ABRecordRef的屬性值。注意在設置ABRecordRef的值時又分為單值屬性和多值屬性:單值屬性設置只要通過ABRecordSetValue()方法指定屬性名和值即可;多值屬性則要先通過創建一個ABMutableMultiValueRef類型的變量,然後通過ABMultiValueAddValueAndLabel()方法依次添加屬性值,最後通過ABRecordSetValue()方法將ABMutableMultiValueRef類型的變量設置為記錄值。
ABRecordRemoveValue():刪除指定的屬性值。
注意:
由於聯系人訪問時(讀取、設置、刪除時)牽扯到大量聯系人屬性,可以到ABPerson.h中查詢或者直接到幫助文檔“Personal Information Properties”
通訊錄的訪問步驟一般如下:
調用ABAddressBookCreateWithOptions()方法創建通訊錄對象ABAddressBookRef。 調用ABAddressBookRequestAccessWithCompletion()方法獲得用戶授權訪問通訊錄。 調用ABAddressBookCopyArrayOfAllPeople()、ABAddressBookCopyPeopleWithName()方法查詢聯系人信息。 調用ABAddressBookCopyArrayOfAllPeople()、ABAddressBookCopyPeopleWithName()方法查詢聯系人信息。 也就是說如果要修改或者刪除都需要首先查詢對應的聯系人,然後修改或刪除後提交更改。如果用戶要增加一個聯系人則不用進行查詢,直接調用ABPersonCreate()方法創建一個ABRecord然後設置具體的屬性,調用ABAddressBookAddRecord方法添加即可下面就通過一個示例演示一下如何通過ABAddressBook.framework訪問通訊錄,這個例子中通過一個UITableViewController模擬一下通訊錄的查看、刪除、添加操作。
主控制器視圖,用於顯示聯系人,修改刪除聯系人:
KCContactViewController.h
#import
/**
* 定義一個協議作為代理
*/
@protocol KCContactDelegate
//新增或修改聯系人
-(void)editPersonWithFirstName:(NSString *)firstName lastName:(NSString *)lastName workNumber:(NSString *)workNumber;
//取消修改或新增
-(void)cancelEdit;
@end
@interface KCContactTableViewController : UITableViewController
@end
KCContactViewController.m
#import "KCContactTableViewController.h"
#import
#import "KCAddPersonViewController.h"
@interface KCContactTableViewController ()
@property (assign,nonatomic) ABAddressBookRef addressBook;//通訊錄
@property (strong,nonatomic) NSMutableArray *allPerson;//通訊錄所有人員
@property (assign,nonatomic) int isModify;//標識是修改還是新增,通過選擇cell進行導航則認為是修改,否則視為新增
@property (assign,nonatomic) UITableViewCell *selectedCell;//當前選中的單元格
@end
@implementation KCContactTableViewController
#pragma mark - 控制器視圖
- (void)viewDidLoad {
[super viewDidLoad];
//請求訪問通訊錄並初始化數據
[self requestAddressBook];
}
//由於在整個視圖控制器周期內addressBook都駐留在內存中,所有當控制器視圖銷毀時銷毀該對象
-(void)dealloc{
if (self.addressBook!=NULL) {
CFRelease(self.addressBook);
}
}
#pragma mark - UI事件
//點擊刪除按鈕
- (IBAction)trashClick:(UIBarButtonItem *)sender {
self.tableView.editing=!self.tableView.editing;
}
#pragma mark - UITableView數據源方法
- (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);//注意這裡進行了強轉,不用自己釋放資源
NSString *lastName=(__bridge NSString *)ABRecordCopyValue(recordRef, kABPersonLastNameProperty);
ABMultiValueRef phoneNumbersRef= ABRecordCopyValue(recordRef, kABPersonPhoneProperty);//獲取手機號,注意手機號是ABMultiValueRef類,有可能有多條
// NSArray *phoneNumbers=(__bridge NSArray *)ABMultiValueCopyArrayOfAllValues(phoneNumbersRef);//取得CFArraryRef類型的手機記錄並轉化為NSArrary
long count= ABMultiValueGetCount(phoneNumbersRef);
// for(int i=0;i0) {
cell.detailTextLabel.text=(__bridge NSString *)(ABMultiValueCopyValueAtIndex(phoneNumbersRef, 0));
}
if(ABPersonHasImageData(recordRef)){//如果有照片數據
NSData *imageData= (__bridge NSData *)(ABPersonCopyImageData(recordRef));
cell.imageView.image=[UIImage imageWithData:imageData];
}else{
cell.imageView.image=[UIImage imageNamed:@"avatar"];//沒有圖片使用默認頭像
}
//使用cell的tag存儲記錄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];//從數組移除
[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;
//根據導航控制器取得添加/修改人員的控制器視圖
KCAddPersonViewController *addPersonController=(KCAddPersonViewController *)navigationController.topViewController;
addPersonController.delegate=self;
//如果是通過選擇cell進行的導航操作說明是修改,否則為添加
if (self.isModify) {
UITableViewCell *cell=self.selectedCell;
addPersonController.recordID=(ABRecordID)cell.tag;//設置
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];//重新初始化數據
[self.tableView reloadData];
}
self.isModify=0;
}
-(void)cancelEdit{
self.isModify=0;
}
#pragma mark - 私有方法
/**
* 請求訪問通訊錄
*/
-(void)requestAddressBook{
//創建通訊錄對象
self.addressBook=ABAddressBookCreateWithOptions(NULL, NULL);
//請求訪問用戶通訊錄,注意無論成功與否block都會調用
ABAddressBookRequestAccessWithCompletion(self.addressBook, ^(bool granted, CFErrorRef error) {
if (!granted) {
NSLog(@"未獲得通訊錄訪問權限!");
}
[self initAllPerson];
});
}
/**
* 取得所有通訊錄記錄
*/
-(void)initAllPerson{
//取得通訊錄訪問授權
ABAuthorizationStatus authorization= ABAddressBookGetAuthorizationStatus();
//如果未獲得授權
if (authorization!=kABAuthorizationStatusAuthorized) {
NSLog(@"尚未獲得通訊錄訪問授權!");
return ;
}
//取得通訊錄中所有人員記錄
CFArrayRef allPeople= ABAddressBookCopyArrayOfAllPeople(self.addressBook);
self.allPerson=(__bridge NSMutableArray *)allPeople;
//釋放資源
CFRelease(allPeople);
}
/**
* 刪除指定的記錄
*
* @param recordRef 要刪除的記錄
*/
-(void)removePersonWithRecord:(ABRecordRef)recordRef{
ABAddressBookRemoveRecord(self.addressBook, recordRef, NULL);//刪除
ABAddressBookSave(self.addressBook, NULL);//刪除之後提交更改
}
/**
* 根據姓名刪除記錄
*/
-(void)removePersonWithName:(NSString *)personName{
CFStringRef personNameRef=(__bridge CFStringRef)(personName);
CFArrayRef recordsRef= ABAddressBookCopyPeopleWithName(self.addressBook, personNameRef);//根據人員姓名查找
CFIndex count= CFArrayGetCount(recordsRef);//取得記錄數
for (CFIndex i=0; i
新增或修改控制器視圖,用於顯示一個聯系人的信息或者新增一個聯系人:
KCAddPersonViewController.h
#import
@protocol KCContactDelegate;
@interface KCAddPersonViewController : UIViewController
@property (assign,nonatomic) int recordID;//通訊錄記錄id,如果ID不為0則代表修改否則認為是新增
@property (strong,nonatomic) NSString *firstNameText;
@property (strong,nonatomic) NSString *lastNameText;
@property (strong,nonatomic) NSString *workPhoneText;
@property (strong,nonatomic) id delegate;
@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 {
//調用代理方法
[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則認為是修改,此時需要初始化界面
self.firstName.text=self.firstNameText;
self.lastName.text=self.lastNameText;
self.workPhone.text=self.workPhoneText;
}
}
@end
運行效果:
備注:
1.上文中所指的以Ref結尾的對象事實上是該對象的指針(或引用),在C語言的框架中多數類型會以Ref結尾,這個類型本身就是一個指針,定義時不需要加“*”。
2.通常方法中包含copy、create、new、retain等關鍵字的方法創建的變量使用之後需要調用對應的release方法釋放。例如:使用ABPersonCreate();創建完ABRecordRef變量後使用CFRelease()方法釋放。
3.在與很多C語言框架交互時可以都存在Obj-C和C語言類型之間的轉化(特別是Obj-C和Core Foundation框架中的一些轉化),此時可能會用到橋接,只要在強轉之後前面加上”__bridge”即可,經過橋接轉化後的類型不需要再去手動維護內存,也就不需要使用對應的release方法釋放內存。
4.AddressBook框架中很多類型的創建、屬性設置等都是以這個類型名開發頭的方法來創建的,事實上如果大家熟悉了其他框架會發現也都是類似的,這是Apple開發中約定俗成的命名規則(特別是C語言框架)。例如:要給ABRecordRef類型的變量設置屬性則可以通過ABRecordSetValue()方法完成。
AddressBookUI
使用AddressBook.framework來操作通訊錄特點就是可以對通訊錄有更加精確的控制,但是缺點就是面對大量C語言API稍嫌麻煩,於是Apple官方提供了另一套框架供開發者使用,那就是AddressBookUI.framework。例如前面查看、新增、修改人員的界面這個框架就提供了現成的控制器視圖供開發者使用。下面是這個框架中提供的控制器視圖:
1. ABPersonViewController:用於查看聯系人信息(可設置編輯)。需要設置displayedPerson屬性來設置要顯示或編輯的聯系人。
2. ABNewPersonViewController:用於新增聯系人信息。
3. ABUnknownPersonViewController:用於顯示一個未知聯系人(尚未保存的聯系人)信息。需要設置displayedPerson屬性來設置要顯示的未知聯系人。
以上三個控制器視圖均繼承於UIViewController,在使用過程中必須使用一個UINavigationController進行包裝,否則只能看到視圖內容無法進行操作(例如對於ABNewPersonViewController如果不使用UINavigationController進行包裝則沒有新增和取消按鈕),同時注意包裝後的控制器視圖不需要處理具體新增、修改邏輯(增加和修改的處理邏輯對應的控制器視圖內部已經完成),但是必須處理控制器的關閉操作(調用dismissViewControllerAnimated::方法),並且可以通過代理方法獲得新增、修改的聯系人。下面看一下三個控制器視圖的代理方法:
ABPersonViewController的displayViewDelegate代理方法:
-(BOOL)personViewController:(ABPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:
``
(ABMultiValueIdentifier)identifier:此方法會在選擇了一個聯系人屬性後觸發,四個參數分別代表:使用的控制器視圖、所查看的聯系人、所選則的聯系人屬性、該屬性是否是多值屬性。
2. ABNewPersonViewController的newPersonViewDelegate代理方法:
-(void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(ABRecordRef)person:點擊取消或完成後觸發,如果參數中的person為NULL說明點擊了取消,否則說明點擊了完成。無論是取消還是完成操作,此方法調用時保存操作已經進行完畢,不需要在此方法中自己保存聯系人信息。
3. ABUnkownPersonViewcontroller的unkownPersonViewDelegate代理方法:
-(void)unknownPersonViewController:(ABUnknownPersonViewController *)unknownCardViewController didResolveToPerson:(ABRecordRef)person:保存此聯系人時調用,調用後將此聯系人返回。
-(BOOL)unknownPersonViewController:(ABUnknownPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier:選擇一個位置聯系人屬性之後執行,返回值代表是否執行默認的選擇操作(例如如果是手機號,默認操作會撥打此電話)
除了上面三類控制器視圖在AddressBookUI中還提供了另外一個控制器視圖ABPeoplePickerNavigationController,它與之前介紹的UIImagePickerController、MPMediaPickerController類似,只是他是用來選擇一個聯系人的。這個控制器視圖本身繼承於UINavigationController,視圖自身的“組”、“取消”按鈕操作不需要開發者來完成(例如開發者不用在點擊取消是關閉當前控制器視圖,它自身已經實現了關閉方法),當然這裡主要說一下這個控制器視圖的peoplePickerDelegate代理方法:
-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person:選擇一個聯系人後執行。此代理方法實現後代理方法“-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier”不會再執行。並且一旦實現了這個代理方法用戶只能選擇到聯系人視圖,無法查看具體聯系人的信息。
-(void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker:用戶點擊取消後執行。
-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier:選擇聯系人具體的屬性後執行,注意如果要執行此方法則不能實現-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person代理方法,此時如果點擊一個具體聯系人會導航到聯系人詳細信息界面,用戶點擊具體的屬性後觸發此方法。
下面就看一下上面四個控制器視圖的使用方法,在下面的程序中定義了四個按鈕,點擊不同的按鈕調用不同的控制器視圖用於演示:
#import "ViewController.h"
#import
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
#pragma mark - UI事件
//添加聯系人
- (IBAction)addPersonClick:(UIButton *)sender {
ABNewPersonViewController *newPersonController=[[ABNewPersonViewController alloc]init];
//設置代理
newPersonController.newPersonViewDelegate=self;
//注意ABNewPersonViewController必須包裝一層UINavigationController才能使用,否則不會出現取消和完成按鈕,無法進行保存等操作
UINavigationController *navigationController=[[UINavigationController alloc]initWithRootViewController:newPersonController];
[self presentViewController:navigationController animated:YES completion:nil];
}
//
- (IBAction)unknownPersonClick:(UIButton *)sender {
ABUnknownPersonViewController *unknownPersonController=[[ABUnknownPersonViewController alloc]init];
//設置未知人員
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;
//設置代理
unknownPersonController.unknownPersonViewDelegate=self;
//設置其他屬性
unknownPersonController.allowsActions=YES;//顯示標准操作按鈕
unknownPersonController.allowsAddingToAddressBook=YES;//是否允許將聯系人添加到地址簿
CFRelease(multiValueRef);
CFRelease(recordRef);
//使用導航控制器包裝
UINavigationController *navigationController=[[UINavigationController alloc]initWithRootViewController:unknownPersonController];
[self presentViewController:navigationController animated:YES completion:nil];
}
- (IBAction)showPersonClick:(UIButton *)sender {
ABPersonViewController *personController=[[ABPersonViewController alloc]init];
//設置聯系人
ABAddressBookRef addressBook=ABAddressBookCreateWithOptions(NULL, NULL);
ABRecordRef recordRef= ABAddressBookGetPersonWithRecordID(addressBook, 1);//取得id為1的聯系人記錄
personController.displayedPerson=recordRef;
//設置代理
personController.personViewDelegate=self;
//設置其他屬性
personController.allowsActions=YES;//是否顯示發送信息、共享聯系人等按鈕
personController.allowsEditing=YES;//允許編輯
// personController.displayedProperties=@[@(kABPersonFirstNameProperty),@(kABPersonLastNameProperty)];//顯示的聯系人屬性信息,默認顯示所有信息
//使用導航控制器包裝
UINavigationController *navigationController=[[UINavigationController alloc]initWithRootViewController:personController];
[self presentViewController:navigationController animated:YES completion:nil];
}
- (IBAction)selectPersonClick:(UIButton *)sender {
ABPeoplePickerNavigationController *peoplePickerController=[[ABPeoplePickerNavigationController alloc]init];
//設置代理
peoplePickerController.peoplePickerDelegate=self;
[self presentViewController:peoplePickerController animated:YES completion:nil];
}
#pragma mark - ABNewPersonViewController代理方法
//完成新增(點擊取消和完成按鈕時調用),注意這裡不用做實際的通訊錄增加工作,此代理方法調用時已經完成新增,當保存成功的時候參數中得person會返回保存的記錄,如果點擊取消person為NULL
-(void)newPersonViewController:(ABNewPersonViewController *)newPersonView didCompleteWithNewPerson:(ABRecordRef)person{
//如果有聯系人信息
if (person) {
NSLog(@"%@ 信息保存成功.",(__bridge NSString *)(ABRecordCopyCompositeName(person)));
}else{
NSLog(@"點擊了取消.");
}
//關閉模態視圖窗口
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - ABUnknownPersonViewController代理方法
//保存未知聯系人時觸發
-(void)unknownPersonViewController:(ABUnknownPersonViewController *)unknownCardViewController didResolveToPerson:(ABRecordRef)person{
if (person) {
NSLog(@"%@ 信息保存成功!",(__bridge NSString *)(ABRecordCopyCompositeName(person)));
}
[self dismissViewControllerAnimated:YES completion:nil];
}
//選擇一個人員屬性後觸發,返回值YES表示觸發默認行為操作,否則執行代理中自定義的操作
-(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代理方法
//選擇一個人員屬性後觸發,返回值YES表示觸發默認行為操作,否則執行代理中自定義的操作
-(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代理方法
//選擇一個聯系人後,注意這個代理方法實現後屬性選擇的方法將不會再調用
-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person{
if (person) {
NSLog(@"選擇了%@.",(__bridge NSString *)(ABRecordCopyCompositeName(person)));
}
}
//選擇屬性之後,注意如果上面的代理方法實現後此方法不會被調用
//-(void)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker didSelectPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
// if (person && property) {
// NSLog(@"選擇了屬性:%i,值:%@.",property,(__bridge NSString *)ABRecordCopyValue(person, property));
// }
//}
//點擊取消按鈕
-(void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{
NSLog(@"取消選擇.");
}
@end
注意:
為了讓大家可以更加清楚的看到幾個控制器視圖的使用,這裡並沒有結合前面的UITableViewController來使用,事實上大家結合前面UITableViewController可以做一個完善的通訊錄應用。