1. 程式人生 > >iOS開發系列--通訊錄、藍芽、內購、GameCenter、iCloud、Passbook系統服務開發彙總

iOS開發系列--通訊錄、藍芽、內購、GameCenter、iCloud、Passbook系統服務開發彙總

--系統應用與系統服務

iOS開發過程中有時候難免會使用iOS內建的一些應用軟體和服務,例如QQ通訊錄、微信電話本會使用iOS的通訊錄,一些第三方軟體會在應用內傳送簡訊等。今天將和大家一起學習如何使用系統應用、使用系統服務:

系統應用

在開發某些應用時可能希望能夠呼叫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 *[email protected]"18500138888"; // NSString *url=[NSString stringWithFormat:@"tel://%@",phoneNumber];//這種方式會直接撥打電話 NSString *url=[NSString stringWithFormat:@"telprompt://%@",phoneNumber];//這種方式會提示使用者確認是否撥打電話 [self openUrl:url]; }
//傳送簡訊 - (IBAction)sendMessageClick:(UIButton *)sender { NSString *[email protected]"18500138888"; NSString *url=[NSString stringWithFormat:@"sms://%@",phoneNumber]; [self openUrl:url]; } //傳送郵件 - (IBAction)sendEmailClick:(UIButton *)sender { NSString *[email protected]"[email protected]"; NSString *url=[NSString stringWithFormat:@"mailto://%@",mailAddress]; [self openUrl:url]; } //瀏覽網頁 - (IBAction)browserClick:(UIButton *)sender { NSString *[email protected]"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的唯一標識,如下圖:

iOSApplication_URLTypes

然後就可以呼叫openURL方法像開啟系統應用一樣開啟第三方應用程式了:

//開啟第三方應用
- (IBAction)thirdPartyApplicationClick:(UIButton *)sender {
    NSString *[email protected]"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使用步驟:

  1. 建立MFMessageComposeViewController物件。
  2. 設定收件人recipients、資訊正文body,如果執行商支援主題和附件的話可以設定主題subject、附件attachments(可以通過canSendSubject、canSendAttachments方法判斷是否支援)
  3. 設定代理messageComposeDelegate(注意這裡不是delegate屬性,因為delegate屬性已經留給UINavigationController,MFMessageComposeViewController沒有覆蓋此屬性而是重新定義了一個代理),實現代理方法獲得傳送狀態。

下面自定義一個傳送簡訊的介面演示MFMessageComposeViewController的使用:

MFMessageComposeViewController_Layout

使用者通過在此介面輸入簡訊資訊點選“傳送資訊”呼叫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

執行效果:

MFMailComposeViewController_Layout     MFMailComposeViewController

通訊錄

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

通訊錄的訪問步驟一般如下:

  1. 呼叫ABAddressBookCreateWithOptions()方法建立通訊錄物件ABAddressBookRef。
  2. 呼叫ABAddressBookRequestAccessWithCompletion()方法獲得使用者授權訪問通訊錄。
  3. 呼叫ABAddressBookCopyArrayOfAllPeople()、ABAddressBookCopyPeopleWithName()方法查詢聯絡人資訊。
  4. 讀取聯絡人後如果要顯示聯絡人資訊則可以呼叫ABRecord相關方法讀取相應的資料;如果要進行修改聯絡人資訊,則可以使用對應的方法修改ABRecord資訊,然後呼叫ABAddressBookSave()方法提交修改;如果要刪除聯絡人,則可以呼叫ABAddressBookRemoveRecord()方法刪除,然後呼叫ABAddressBookSave()提交修改操作。
  5. 也就是說如果要修改或者刪除都需要首先查詢對應的聯絡人,然後修改或刪除後提交更改。如果使用者要增加一個聯絡人則不用進行查詢,直接呼叫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 *[email protected]"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 {