連載
» 2011年04月20日 00時00分 公開

SDKで始めるiPad/iPhoneアプリ開発の勘所(終):iOS 4の新機能13選&AssetsLibraryで作る画像ビューア (3/4)

[竹内彰吾,株式会社ビーブレイクシステムズ]

「フォト選択」画面を作り、画像を一覧表示

画像選択のコントローラを作成

 「PhotoGroupViewController.m」に以下のようにコードを追加します。

#import "PhotoSelectViewController.h"
    
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    PhotoSelectViewController *controller = [[PhotoSelectViewController alloc] init];
    controller.delegate = self;
    controller.group = [groups objectAtIndex:indexPath.row];
    [self.navigationController pushViewController:controller animated:YES];
    [controller release];
}
PhotoGroupViewController.m

 次に、[PhotoSelectViewController]をXcodeのグループとファイルに新規作成します。

#import "PhotoViewController.h"
    
@interface PhotoSelectViewController : UITableViewController<PhotoViewControllerDelegate> {
    id delegate;
    ALAssetsGroup *group;
    @private
    NSMutableArray *photos;
}
    
@property (nonatomic, assign) id delegate;
@property (nonatomic, retain) ALAssetsGroup *group;
    
@end
PhotoSelectViewController.h

 メンバ変数groupは、呼び出し元のPhotoGroupViewControllerのテーブルから選択された行のALAssetsGroupを保持します。

ALAssetクラスの使い方

 メンバ変数photosは、AssetsLibraryフレームワークで定義されたALAssetクラスの集合を保持します。ALAssetクラスはフォトライブラリ内の静止画、または動画を保持しています。

#import "PhotoSelectViewController.h"
    
@implementation PhotoSelectViewController
    
@synthesize delegate;
@synthesize group;
    
#pragma mark Rotation support
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return YES;
}
    
#pragma mark View lifecycle
- (void)viewDidLoad {
    [super viewDidLoad];
    [self.navigationItem setTitle:@"フォト選択"];
    [self performSelectorInBackground:@selector(searchPhotos) withObject:nil];
}
    
- (void)searchPhotos {
    photos = [[NSMutableArray alloc] init];
    // フォトライブラリから取得した写真を「photos」に追加
    void (^assetBlock)(struct ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *result, NSUInteger index, BOOL *stop) {
        if(result != nil) {
            if(![photos containsObject:result]) {
                // ALAssetのタイプが「写真」を追加
                if([[result valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypePhoto]) {
                    [photos addObject:result];
                }
            }
        }
    };
    // フォトライブラリへアクセスし、引数のブロックを実行
    [group enumerateAssetsUsingBlock:assetBlock];
    [self.tableView reloadData];
}
 
// テーブル指定行の画像データを取得
- (UIImage *)getPhotoImage:(NSUInteger)index {
    return [UIImage imageWithCGImage:
        [[(ALAsset *)[photos objectAtIndex:index] defaultRepresentation] fullScreenImage]];
}
 
// テーブル指定行のメタデータを取得
- (NSDictionary *)getPhotoMetaData:(NSUInteger)index {
    NSDictionary *metaData = [[(ALAsset *)[photos objectAtIndex:index] defaultRepresentation] metadata];
    return [metaData objectForKey:(NSString *)kCGImagePropertyExifDictionary];
}
 
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [photos 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 = [self getPhotoImage:indexPath.row];
    NSDictionary *metadata = [self getPhotoMetaData:indexPath.row];
    NSString *title = [metadata objectForKey:(NSString *)kCGImagePropertyExifUserComment];
    cell.imageView.image = image;
    cell.textLabel.text = [NSString stringWithFormat:@"%@", title];
    return cell;
}
 
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [self photoSelect:[self getPhotoImage:indexPath.row]];
}
 
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 300;
}
 
#pragma mark PhotoViewControllerDelegate Methods
- (void)photoSelect:(UIImage *)image {
    [delegate photoSelect:image];
}
 
- (void)photoSelectCancel {
    [delegate photoSelectCancel];
}
 
#pragma mark Memory management
- (void)dealloc {
    [super dealloc];
}
    
@end
PhotoSelectViewController.m

 「searchPhotos」メソッドでは、「assetBlock」というブロック構文をALAssetGroupのenumerateAssetsUsingBlockメソッドに渡すことでグループ内の写真データをphotos変数に追加しています。

 ALAssetクラスは写真だけでなく動画データも保持しており、valueForPropertyメソッドの引数にALAssetPropertyTypeを渡すことでAssetの種類が取得できます。Assetの種類には以下の3つがあります。

  • ALAssetTypePhoto:写真
  • ALAssetTypeVideo:動画
  • ALAssetTypeUnknown:不明な種類

 ValueForPropertyメソッドでは上記以外にも「位置情報」や「撮影日」などが必要に応じて取得できます。ALAssetクラスの公式リファレンスにも詳細が記載されています。

 「getPhotoImage : index」メソッドでは、photos配列のindexを指定することで、ALAssetからテーブル選択行の画像データ(UIImage)を取得しています。

 ALAssetのdefaultRepresentationメソッドで実際のメタデータや画像データを保持したオブジェクトを取得し、その中からfullScreenImageメソッドでフォトライブリに保存されたフル画像を取得しています。

メタデータを取得

 「getPhotoMetaData : index」メソッドでは、getPhotoImageメソッドと同様に、defaultRepresentationメソッドを使用してメタデータを取得しています。

 さらに、このメタデータの中のExif情報のみを、「ImageIO」フレームワークで定義された「kCGImagePropertyExifDictionary」という定数で取得しています。

Exif情報を取得

 「tableView : cellForRowAtIndexPath : indexPath」メソッドでは、メンバ変数photosから行ごとのALAssetを取得し、写真画像・写真タイトル(Exif情報)を取得し、セルにセットしています。

 写真タイトルは「kCGImagePropertyExifUserComment」というコメントを表した定数を使用していますが、これは後ほど写真タイトルをセットする実装を行う際に、この定数を使用しているためです。

いったん実行して確認

 ここまで実装が完了したら、ビルドして実行してみましょう。「グループ選択」画面から行を選択すると「フォト選択」画面が現れ、フォトライブラリから取得した写真が一覧で表示されます。

 次ページでは、写真がテーブルから選択された後の処理として、読み込んだ画像にも絵を描けるようにする機能とExif情報に絵のタイトルを設定する機能を実装し、アプリを完成します。

Copyright © ITmedia, Inc. All Rights Reserved.

編集部からのお知らせ

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。