Kesinの知見置き場

知見を共有していきたいじゃないですか

iOSアプリでアドレスブックを利用する

以前参加したインターンシップiPhoneのアドレスブックからメールアドレスを選択してメールを送るという機能を実装したのですが、アドレスブックにアクセスする方法の情報が書籍にもネットにも意外に少なかったので紹介したいと思います。


執筆時ではXcode4.4.1でARCを使うコードとなっています。Objective-CXcodeの進化は速いのでこのバージョンより新しい(古い)Xcodeではこの記事の通りにやっても動かない可能性があります。ご注意ください。

1. iPhoneシミュレーターでデバッグするために

まず、iPhoneシミュレーターのアドレスブックは登録したことがなければ1件も登録されていないので、何か適当なアドレスを登録します。方法は実機と同じで、シミュレーターのホームボタンをクリックしてアドレスブックを起動していつものようにアドレスを登録するだけです。シミュレーターを使わずに実機でデバッグするならこの作業は不要です

2. 簡単なアドレスブック連携アプリ


アドレスブックの使い方を紹介するために、こんな感じのテーブルビューにボタン二つという簡単なアプリを作ってみたいと思います。+ボタンでアドレスブックを呼び出して一人一つのメールアドレスを追加し、Editボタンで削除を行えるようにします。
メールアドレスは苗字・名前と違って一人に複数存在可能性があるので、複数存在した場合はどれか一つのアドレスを選択させるような処理にします。


アプリからアドレスブックにアクセスするにはまずプロジェクトにAddressBook.framework, AddressBookUI.frameworkを追加します。そしてヘッダーにのデリゲートを追加し、必要なデリゲートメソッドに処理を書くだけです。

peoplePickerを呼び出す

まずはpeoplePicker(アドレスブックから選択するビュー)の呼び出しを行います。

    ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc]init];
    picker.peoplePickerDelegate = self;
    [self presentModalViewController:picker animated:YES];
-(void)peoplePickerNavigationControllerDidCancel:

peoplePickerがキャンセルされたときの処理です。基本的にはモーダルを閉じる処理を追加します

-(BOOL)peoplPickerNavigationController:shouldContinueAfterSelectingPerson:


peoplePickerで誰かを選択したときの処理です。戻り値はYESなら詳細画面に進み、NOなら何も起こりません。引数のpersonが選択された人物なので、苗字や名前など一人に一つしかありえない値は簡単に取得できます。
メールアドレスなどの一人に複数あり得る値のうち一つをユーザーに選択させたい場合は、次の詳細画面で選択させることになります。詳細画面で余計な情報を表示させないために、return YESの前に[people Picker setDisplayedPropertie:]で必要な情報だけ(例えばメールアドレスなど)表示させるようにした方がいいでしょう

-(BOOL)peoplePickerNavigationController:shouldContinueAfterSelectingPerson: property: identifier:


詳細画面で選択したときの処理です。引数であるpropertyはどの種類が選択されたか、identifierは何番目が選択されたかを表します。propertyとidentiriferを使うことで例えば2つ目のメールアドレスが選択された、ということを特定することができます。
戻り値はYESなら種類に応じたデフォルトの動作(電話をかける、メール送るなど)を行い、NOなら何も起こりません。

コード例

アドレスブックで選択した人物のメールアドレスをテーブルビューに表示し、Editボタンで削除を行えるようにするとこんな感じです。デフォルトのコメントもところどころ残っていたりしますが気にしないでください^^;

AddressTableViewController.h

#import <UIKit/UIKit.h>
#import <AddressBookUI/AddressBookUI.h>
#import <AddressBook/AddressBook.h>

@interface AddressTableViewController : UITableViewController<ABPeoplePickerNavigationControllerDelegate>

- (IBAction)addPerson:(id)sender;
- (IBAction)editAddress:(id)sender;
@property (strong, nonatomic) IBOutlet UITableView *addressTable;

@end

AddressTableViewController.m

#import "AddressTableViewController.h"

@interface AddressTableViewController ()

@end

@implementation AddressTableViewController{
    NSMutableArray *persons; //テーブルの表示に使用する
    NSString *firstName;
    NSString *lastName;
    BOOL isEditing; //編集モード中であるか
}
@synthesize addressTable;

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    persons = [NSMutableArray array];
    isEditing = NO;
}

- (void)viewDidUnload
{
    [self setAddressTable:nil];
    [super viewDidUnload];
}

//ビューが表示されるたびにテーブルビューの更新を行う
-(void)viewWillAppear:(BOOL)animated{
    [addressTable reloadData];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return persons.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Person";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
   
    cell.textLabel.text = [NSString stringWithFormat:@"%@ %@",
                           [[persons objectAtIndex:indexPath.row]objectForKey:@"first"],
                           [[persons objectAtIndex:indexPath.row]objectForKey:@"last"]];
    return cell;
}

//編集モード
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        //削除時の操作 選択された行に対応するデータをpersonsから消去
        [persons removeObjectAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
        if (persons.count<1) {
            [addressTable setEditing:NO animated:YES];
        }
    }
}

//テーブルビューの項目を選択したとき
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"First: %@", [[persons objectAtIndex:indexPath.row]objectForKey:@"first"]);
    NSLog(@"Last: %@", [[persons objectAtIndex:indexPath.row]objectForKey:@"last"]);
    NSLog(@"Email: %@", [[persons objectAtIndex:indexPath.row]objectForKey:@"email"]);
}

//Editボタンを押した時に編集モードのON, OFFを行う
- (IBAction)editAddress:(id)sender {
    if(isEditing){
        [addressTable setEditing:NO animated:YES];
        isEditing = NO;
    }
    else{
        [addressTable setEditing:YES animated:YES];
        isEditing = YES;
    }
}

//peoplePickerモーダルを開く
- (IBAction)addPerson:(id)sender {
    ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc]init];
    picker.peoplePickerDelegate = self;
    [self presentModalViewController:picker animated:YES];
}

//peoplePickerをキャンセル
- (void) peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{
    [self dismissModalViewControllerAnimated:YES];
}

//peoplePickerで誰かを選択した
- (BOOL) peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person{
    ABMultiValueRef addrs = (ABMultiValueRef)ABRecordCopyValue(person,kABPersonEmailProperty);
    //選択した人物が持つメールアドレスの数
    int addrCount = ABMultiValueGetCount(addrs);
    firstName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonFirstNameProperty));
    lastName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonLastNameProperty));
    NSLog(@"%@ %@", lastName, firstName);
   
    //メールアドレスが1つもない
    if (addrCount==0){
        NSLog(@"メールアドレスが登録されていません");
        return NO;
    }
    //メールアドレスが複数ある
    else if(addrCount>1){
        //次の詳細画面にemailアドレスしか出ないようにセット。return YESで詳細画面に進む
        [peoplePicker setDisplayedProperties:[NSArray arrayWithObject:[NSNumber numberWithInt:kABPersonEmailProperty]]];
        return YES;
    }
    //メールアドレスが1つしかない
    else{
        NSString *email = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(addrs, 0);
        NSDictionary *selectedPerson = @{@"first":firstName, @"last":lastName, @"email":email};
        [persons addObject:selectedPerson];
    }
    [self dismissModalViewControllerAnimated:YES];
    return NO;
}

//詳細画面で選択したとき
//メールアドレスを複数持つ人は一つのアドレスだけを選択
- (BOOL) peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
    ABMutableMultiValueRef *multi = ABRecordCopyValue(person, property);
    CFIndex index = ABMultiValueGetIndexForIdentifier(multi, identifier);
   
    //選択されたアドレスと人物を紐付けてpersonsに追加
    NSString *email = (__bridge NSString *)ABMultiValueCopyValueAtIndex(multi, index);
    NSDictionary *selectedPerson = @{@"first":firstName, @"last":lastName, @"email":email};
    [persons addObject:selectedPerson];
   
    [self dismissModalViewControllerAnimated:YES];
    return NO;
}

@end