--系統應用與系統服務 iOS開發過程中有時候難免會使用iOS內置的一些應用軟體和服務,例如QQ通訊錄、微信電話本會使用iOS的通訊錄,一些第三方軟體會在應用內發送簡訊等。今天將和大家一起學習如何使用系統應用、使用系統服務:http://www.jinhusns.com/Products/Downl ...
iOS開發過程中有時候難免會使用iOS內置的一些應用軟體和服務,例如QQ通訊錄、微信電話本會使用iOS的通訊錄,一些第三方軟體會在應用內發送簡訊等。今天將和大家一起學習如何使用系統應用、使用系統服務:http://www.jinhusns.com/Products/Download/?type=xcj
系統應用
在開發某些應用時可能希望能夠調用iOS系統內置的電話、簡訊、郵件、瀏覽器應用,此時你可以直接使用UIApplication的OpenURL:方法指定特定的協議來打開不同的系統應用。常用的協議如下:
打電話:tel:或者tel://、telprompt:或telprompt://(撥打電話前有提示)
發簡訊:sms:或者sms://
發送郵件:mailto:或者mailto://
啟動瀏覽器:http:或者http://
下麵以一個簡單的demo演示如何調用上面幾種系統應用:
// // ViewController.m // iOSSystemApplication // // Created by Kenshin Cui on 14/04/05. // Copyright (c) 2014年 cmjstudio. All rights reserved. //#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的使用:
用戶通過在此界面輸入簡訊信息點擊“發送信息”調用MFMessageComposeViewController界面來展示或進一步編輯信息,點 擊MFMessageComposeViewController中的“發送”來完成簡訊發送工作,當然用戶也可能點擊“取消”按鈕回到前一個簡訊編輯頁 面。
實現代碼:
// // KCSendMessageViewController.m // iOSSystemApplication // // Created by Kenshin Cui on 14/04/05. // Copyright (c) 2014年 cmjstudio. All rights reserved. //#import "KCSendMessageViewController.h"#import <MessageUI/MessageUI.h>@interface KCSendMessageViewController ()<MFMessageComposeViewControllerDelegate> @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
這裡需要強調一下:
-
MFMessageComposeViewController的代理不是通過delegate屬性指定的而是通過messageComposeDelegate指定的。
-
可以通過幾種方式來指定發送的附件,在這個過程中請務必指定文件的尾碼,否則在發送後無法正確識別文件類別(例如如果發送的是一張jpg圖片,在發送後無法正確查看圖片)。
-
無論發送成功與否代理方法-(void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result都會執行,通過代理參數中的result來獲得發送狀態。
其實只要熟悉了 MFMessageComposeViewController之後,那麼用於發送郵件的MFMailComposeViewController用法和 步驟完全一致,只是功能不同。下麵看一下MFMailComposeViewController的使用:
// // KCSendEmailViewController.m // iOSSystemApplication // // Created by Kenshin Cui on 14/04/05. // Copyright (c) 2014年 cmjstudio. All rights reserved. //#import "KCSendEmailViewController.h"#import <MessageUI/MessageUI.h>@interface KCSendEmailViewController ()<MFMailComposeViewControllerDelegate> @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
運行效果:
通訊錄
AddressBook
iOS中帶有一個Contacts應用程式來管理聯繫人,但是有些時候我們希望自己的應用能夠訪問或者修改這些信息,這個時候就要用到 AddressBook.framework框架。iOS中的通訊錄是存儲在資料庫中的,由於iOS的許可權設計,開發人員是不允許直接訪問通訊錄資料庫 的,必須依靠AddressBook提供的標準API來實現通訊錄操作。通過AddressBook.framework開發者可以從底層去操作 AddressBook.framework的所有信息,但是需要註意的是這個框架是基於C語言編寫的,無法使用ARC來管理記憶體,開發者需要自己管理內 存。下麵大致介紹一下通訊錄操作中常用的類型:
-
ABAddressBookRef:代表通訊錄對象,通過該對象開發人員不用過多的關註通訊錄的存儲方式,可以直接以透明的方式去訪問、保存(在 使用AddressBook.framework操作聯繫人時,所有的增加、刪除、修改後都必須執行保存操作,類似於Core Data)等。
-
ABRecordRef:代表一個通用的記錄對象,可以是一條聯繫人信息,也可以是一個群組,可以通過 ABRecordGetRecordType()函數獲得具體類型。如果作為聯繫人(事實上也經常使用它作為聯繫人),那麼這個記錄記錄了一個完整的聯繫 人信息(姓名、性別、電話、郵件等),每條記錄都有一個唯一的ID標示這條記錄(可以通過ABRecordGetRecordID()函數獲得)。
-
ABPersonRef:代表聯繫人信息,很少直接使用,實際開發過程中通常會使用類型為“kABPersonType”的ABRecordRef來表示聯繫人(由此可見ABPersonRef其實是一種類型為“kABPersonType”的ABRecordRef)
-
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()方法查詢聯繫人信息。
-
讀取聯繫人後如果要顯示聯繫人信息則可以調用ABRecord相關方法讀取相應的數據;如果要進行修改聯繫人信息,則可以使用對應的方 法修改ABRecord信息,然後調用ABAddressBookSave()方法提交修改;如果要刪除聯繫人,則可以調用 ABAddressBookRemoveRecord()方法刪除,然後調用ABAddressBookSave()提交修改操作。
-
也就是說如果要修改或者刪除都需要首先查詢對應的聯繫人,然後修改或刪除後提交更改。如果用戶要增加一個聯繫人則不用進行查詢,直接調 用ABPersonCreate()方法創建一個ABRecord然後設置具體的屬性,調用ABAddressBookAddRecord方法添加即可。
下麵就通過一個示例演示一下如何通過ABAddressBook.framework訪問通訊錄,這個例子中通過一個UITableViewController模擬一下通訊錄的查看、刪除、添加操作。
主控制器視圖,用於顯示聯繫人,修改刪除聯繫人:
KCContactViewController.h
// // KCTableViewController.h // AddressBook // // Created by Kenshin Cui on 14/04/05. // Copyright (c) 2014年 cmjstudio. All rights reserved. //#import <UIKit/UIKit.h>/** * 定義一個協議作為代理 */@protocol KCContactDelegate//新增或修改聯繫人-(void)editPersonWithFirstName:(NSString *)firstName lastName:(NSString *)lastName workNumber:(NSString *)workNumber;//取消修改或新增-(void)cancelEdit; @end @interface KCContactTableViewController : UITableViewController @end
KCContactViewController.m
// // KCTableViewController.m // AddressBook // // Created by Kenshin Cui on 14/04/05. // Copyright (c) 2014年 cmjstudio. All rights reserved. //#import "KCContactTableViewController.h"#import <AddressBook/AddressBook.h>#import "KCAddPersonViewController.h"@interface KCContactTableViewController ()<KCContactDelegate> @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;i<count;++i){ // NSString *phoneLabel= (__bridge NSString *)(ABMultiValueCopyLabelAtIndex(phoneNumbersRef, i)); // NSString *phoneNumber=(__bridge NSString *)(ABMultiValueCopyValueAtIndex(phoneNumbersRef, i)); // NSLog(@"%@:%@",phoneLabel,phoneNumber); // } cell.textLabel.text=[NSString stringWithFormat:@"%@ %@",firstName,lastName]; if (count>0) { 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<count; ++i) { ABRecordRef recordRef=CFArrayGetValueAtIndex(recordsRef, i);//取得指定的記錄 ABAddressBookRemoveRecord(self.addressBook, recordRef, NULL);//刪除 } ABAddressBookSave(self.addressBook, NULL);//刪除之後提交更改 CFRelease(recordsRef); }/** * 添加一條記錄 * * @param firstName 名 * @param lastName 姓 * @param iPhoneName iPhone手機號 */-(void)addPersonWithFirstName:(NSString *)firstName lastName:(NSString *)lastName workNumber:(NSString *)workNumber{ //創建一條記錄 ABRecordRef recordRef= ABPersonCreate(); ABRecordSetValue(recordRef, kABPersonFirstNameProperty, (__bridge CFTypeRef)(firstName), NULL);//添加名 ABRecordSetValue(recordRef, kABPersonLastNameProperty, (__bridge CFTypeRef)(lastName), NULL);//添加姓 ABMutableMultiValueRef multiValueRef =ABMultiValueCreateMutable(kABStringPropertyType);//添加設置多值屬性 ABMultiValueAddValueAndLabel(multiValueRef, (__bridge CFStringRef)(workNumber), kABWorkLabel, NULL);//添加工作電話 ABRecordSetValue(recordRef, kABPersonPhoneProperty, multiValueRef, NULL); //添加記錄 ABAddressBookAddRecord(self.addressBook, recordRef, NULL); //保存通訊錄,提交更改 ABAddressBookSave(self.addressBook, NULL); //釋放資源 CFRelease(recordRef); CFRelease(multiValueRef); }/** * 根據RecordID修改聯繫人信息 * * @param recordID 記錄唯一ID * @param firstName 姓 * @param lastName 名 * @param homeNumber 工作電話 */-(void)modifyPersonWithRecordID:(ABRecordID)recordID firstName:(NSString *)firstName lastName:(NSString *)lastName workNumber:(NSString *)workNumber{ ABRecordRef recordRef=ABAddressBookGetPersonWithRecordID(self.addressBook,recordID); ABRecordSetValue(recordRef, kABPersonFirstNameProperty, (__bridge CFTypeRef)(firstName), NULL);//添加名 ABRecordSetValue(recordRef, kABPersonLastNameProperty, (__bridge CFTypeRef)(lastName), NULL);//添加姓 ABMutableMultiValueRef multiValueRef =ABMultiValueCreateMutable(kABStringPropertyType); ABMultiValueAddValueAndLabel(multiValueRef, (__bridge CFStringRef)(workNumber), kABWorkLabel, NULL); ABRecordSetValue(recordRef, kABPersonPhoneProperty, multiValueRef, NULL); //保存記錄,提交更改 ABAddressBookSave(self.addressBook, NULL); //釋放資源 CFRelease(multiValueRef); } @end
新增或修改控制器視圖,用於顯示一個聯繫人的信息或者新增一個聯繫人:
KCAddPersonViewController.h
// // KCAddPersonViewController.h // AddressBook // // kABPersonFirstNameProperty // Copyright (c) 2014年 cmjstudio. All rights reserved. //#import <UIKit/UIKit.h>@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<KCContactDelegate> delegate; @end
KCAddPersonViewController.m
// // KCAddPersonViewController.m // AddressBook // // kABPersonFirstNameProperty // Copyright (c) 2014年 cmjstudio. All rights reserved. //#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。例如前面查看、新增、修改人員的界面這個框架 就提供了現成的控制器視圖供開發者使用。下麵是這個框架中提供的控制器視圖:
-
ABPersonViewController:用於查看聯繫人信息(可設置編輯)。需要設置displayedPerson屬性來設置要顯示或編輯的聯繫人。
-
ABNewPersonViewController:用於新增聯繫人信息。
-
ABUnknownPersonViewController:用於顯示一個未知聯繫人(尚未保存的聯繫人)信息。需要設置displayedPerson屬性來設置要顯示的未知聯繫人。
以上三個控制器視圖均繼承於UIViewController,在使用過程中必須使用一個UINavigationController進行包裝, 否則只能看到視圖內容無法進行操作(例如對於ABNewPersonViewController如果不使用 UINavigationController進行包裝則沒有新增和取消按鈕),同時註意包裝後的控制器視圖不需要處理具體新增、修改邏輯(增加和修改的 處理邏輯對應的控制器視圖內部已經完成),但是必須處理控制器的關閉操作(調用dismissViewControllerAnimated::方法), 並且可以通過代理方法獲得新增、修改的聯繫人。下麵看一下三個控制器視圖的代理方法:
1.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代理方法,此時如果點擊一個具體聯繫人會導航到聯繫人詳細信息界面,用戶點擊具 體的屬性後觸發此方法。
下麵就看一下上面四個控制器視圖的使用方法,在下麵的程式中定義了四個按鈕,點擊不同的按鈕調用不同的控制器視圖用於演示:
// // ViewController.m // AddressBookUI // // Created by Kenshin Cui on 14/04/05. // Copyright (c) 2014年 cmjstudio. All rights reserved. //#import "ViewController.h"#import <AddressBookUI/AddressBookUI.h>@interface ViewController ()<ABNewPersonViewControllerDelegate,ABUnknownPersonViewControllerDelegate,ABPeoplePickerNavigationControllerDelegate,ABPersonViewControllerDelegate> @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 *)(ABRecordCopyCompositeN