iOS 4で追加された「AssetsLibraryフレームワーク」を使ってフォトライブリから写真を読み込み、付加情報を付けて保存をする方法について解説します。連載第5回の「Core Graphicsで作るiPad向けお絵かきアプリの基礎」で作成したお絵かきアプリに対して、これらの機能を追加していきましょう。
第5回記事では、「UIImageWriteToSavedPhotosAlbum」関数を用いてアプリ内からフォトアルバムへ写真を追加する方法を解説しました。ここまではiOS 4以前でも可能でしたが、新しく追加されたAssetsLibraryフレームワークを使用すると、以下のことが可能になります。
それでは、お絵かきアプリのツールバーに新しく検索ボタンを追加し、このボタンを押すと、フォトライブラリ内の全画像データをテーブルに表示する画面を作成していきましょう。
第5回のお絵かきアプリのプロジェクト/fsmart/articles/iphonesdk05/SampleOekaki.zipをXcodeで開きます。Xcodeのグループとファイルから[Frameworks]を右クリックし、[追加]→[既存のフレームワーク]を選択します。
表示されたフレームワークの一覧から[AssetsLibrary.framework][ImageIO.framework]の2つを選択し、[追加]をクリックします。
以上で、プロジェクトに[AssetsLibrary.framework][ImageIO.framework]が追加されました。なお、ImageIOはExifを保存する際にキーとなる定数を保持しているため必要となります。
次に、グループとファイルから[Other Sources]→「【アプリ名】_Prefix.pch」を開き、AssetsLibraryとImageIOの共通インポートを追加します。
#ifdef __OBJC__ #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #import <AssetsLibrary/AssetsLibrary.h> #import <ImageIO/ImageIO.h> #endif
「DetailViewController.m」のviewDidLoadメソッドに検索ボタンを追加します。
#import "PhotoViewController.h" ……【省略】…… - (void)viewDidLoad { ……【省略】…… // 写真検索ボタンの生成 UIBarButtonItem *searchtBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSearch target:self action:@selector(searchPhoto:)]; NSMutableArray *items = [[toolbar items] mutableCopy]; [items addObject:cameraBtn]; [items addObject:searchtBtn]; [items addObject:spaceBtn]; [items addObject:titleTextButton]; [items addObject:spaceBtn]; [items addObject:trashBtn]; [toolbar setItems:items animated:NO]; [items release]; [cameraBtn release]; [searchtBtn release]; [spaceBtn release]; [trashBtn release]; } // フォトライブラリ内の写真を表示する -(void)searchPhoto:(id)sender { PhotoViewController *controller = [[PhotoViewController alloc] initController]; controller.delegate = self; [self presentModalViewController:controller animated:YES]; [controller release]; }
searchPhotoメソッドは、写真検索ボタン押下時に呼ばれます。この中では、UINavigaitionControllerを継承したPhotoViewControllerを作成し、モーダルビューとして表示しています。このPhotoViewControllerにフォトライブラリの内容を表示していきます。
次に、「PhotoViewController」をXcodeのグループとファイルに新規作成します。
@protocol PhotoViewControllerDelegate @required // 写真が選択されたときに呼ばれる - (void)photoSelect:(UIImage *)image; // 写真の選択をキャンセルしたときに呼ばれる - (void)photoSelectCancel; @end @interface PhotoViewController : UINavigationController<PhotoViewControllerDelegate> { id delegate; } @property (nonatomic, assign) id delegate; -(id)initController; @end
PhotoViewControllerDelegateプロトコルは呼び出し元のDetailViewController、呼び出し先のViewControllerすべてに実装し、「写真の決定」「選択の取消」が行われた際にDetailViewControllerへ処理が戻るようにしていきます。
#import "PhotoViewController.h" #import "PhotoGroupViewController.h" @implementation PhotoViewController @synthesize delegate; -(id)initController { if(self = [super init]) { // グループ選択テーブルを表示するViewControllerをルートビューとして生成 PhotoGroupViewController *controller = [[PhotoGroupViewController alloc] init]; controller.delegate = self; [super initWithRootViewController:controller]; [controller release]; } return self; } #pragma mark PhotoViewControllerDelegate Methods - (void)photoSelect:(UIImage *)image { [delegate photoSelect:image]; } - (void)photoSelectCancel { [delegate photoSelectCancel]; } #pragma mark Memory management - (void)dealloc { [super dealloc]; } @end
initControllerメソッドでは、UINavigationControllerでテーブル表示を行うため、UITableViewControllerの継承クラスをルートビューに設定しています。
次に、「PhotoGroupViewController.h」をXcodeのグループとファイルに新規作成します。
#import "PhotoViewController.h" @interface PhotoGroupViewController : UITableViewController<PhotoViewControllerDelegate> { id delegate; @private NSMutableArray *groups; ALAssetsLibrary *library; } @property (nonatomic, assign) id delegate; @end
メンバ変数「gorups」は、AssetsLibraryフレームワークで定義されたALAssetsGroupクラスの集合を保持します。ALAssetsGroupクラスはフォトライブラリ内で分けられた1つのグループを表しています。
メンバ変数「library」は、AssetsLibraryフレームワークで定義されたALAssetsLibraryクラスであり、フォトライブラリを操作するためのクラスとなります。
#import "PhotoGroupViewController.h" @implementation PhotoGroupViewController @synthesize delegate; #pragma mark Rotation support - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return YES; } #pragma mark View lifecycle - (void)viewDidLoad { [super viewDidLoad]; [self.navigationItem setTitle:@"グループ選択"]; // 別スレッドでフォトライブラリ内のグループを検索 [self performSelectorInBackground:@selector(searchPhotoGroups) withObject:nil]; // 写真選択のキャンセルボタンを生成 UIBarButtonItem *cancelButton = [ [UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(photoSelectCancel) ]; [self.navigationItem setRightBarButtonItem:cancelButton]; [cancelButton release]; } -(void)searchPhotoGroups { groups = [[NSMutableArray alloc] init]; library = [[ALAssetsLibrary alloc] init]; // フォトライブラリから取得したグループをgroupsに追加。追加するたびに画面のリロードを行う void (^groupBlock)(struct ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) { if(group != nil) [groups addObject:group]; [self performSelectorOnMainThread:@selector(reload) withObject:nil waitUntilDone:NO]; }; // フォトライブラリへアクセスし、引数のブロックを実行する。 [library enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:groupBlock failureBlock:^(NSError *error) { NSLog(@"A problem occured"); }]; } -(void)reload { [self.tableView reloadData]; } #pragma mark Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [groups count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; // フォトライブラリ内のポスター画像・グループ名・データ数を取得し、セルに表示する UIImage *image = [UIImage imageWithCGImage: [(ALAssetsGroup*)[groups objectAtIndex:indexPath.row] posterImage]]; NSString *groupName = [(ALAsset*)[groups objectAtIndex:indexPath.row] valueForProperty:ALAssetsGroupPropertyName]; NSInteger photoCnt = [(ALAssetsGroup*)[groups objectAtIndex:indexPath.row] numberOfAssets]; cell.imageView.image = image; cell.textLabel.text = [NSString stringWithFormat:@"%@ (%d)", groupName, photoCnt]; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; return cell; } #pragma mark Table view delegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 80; } #pragma mark PhotoViewControllerDelegate Methods - (void)photoSelect:(UIImage *)image { [delegate photoSelect:image]; } - (void)photoSelectCancel { [delegate photoSelectCancel]; } #pragma mark Memory management - (void)dealloc { [groups release]; [library release]; [super dealloc]; } @end
「viewDidLoadメソッド」では、performSelectorInBackgroundメソッドを使用して別スレッドでフォトライブラリ内の検索を行っています。別スレッドにしておくことで大量の画像データを検索する場合も画面がスムーズに表示されます。
「searchPhotoGroups」メソッドでは、「groupBlock」というブロック構文をALAssetsLibraryのenumerateGroupWithTypesメソッドに渡すことでフォトライブラリ内のグループデータをgroupsに追加しています。
Objective-C言語のブロック構文については、アップル公式ドキュメント「ブロックプログラミングトピック」をご参照ください。
「tableView : cellForRowAtIndexPath : indexPath」メソッドでは、メンバ変数gorupsからテーブル行ごとのALAssetGroupを取得し、グループに設定されたポスター画像・グループ名・データ数を取得し、セルにセットしています。
ここまで実装が完了したら、ビルドして実行([Command]+[R]キー)してみましょう。カメラボタンの右側に作った検索ボタンを押すと、「グループ選択」画面が現れ、フォトライブラリ内のグループが表示されます。
次ページでは、このグループの中から写真を選択するための一覧画面を作成します。
Copyright © ITmedia, Inc. All Rights Reserved.