Edited at

Photos Framework を使って写真.appと画像をやりとりする

More than 3 years have passed since last update.

iOS8で Photos Framework が追加されました。

Photos Framework は「写真.app」や「iCloud」の写真/ビデオに対し、読み書きやプロパティの編集、変更の監視などができます。

App Extensionが追加されたのもあり、写真やビデオを複数のアプリで扱うための機能が用意されています。


写真.appと画像をやりとりするサンプルを作成してみました

Photos Framework では、 主に3つの階層があってその上でデータをやりとりします。

基本的な読み書きでは、PHAssetCollecitonPHAssetを扱うことになります。

クラス
なにを扱うのか

PHCollectionList
PHAssetCollectionの集まり

PHAssetCollection
カメラロール自体やユーザ作成のフォルダ、モーメントの年など

PHAsset
写真やビデオ自体


アルバムから画像を取得して UIImageView に表示する

- (void)readImage {

// PHAssetCollection を取得します
PHAssestCollection * myAlbum = [self getMyAlbum];

// PHAsset をフェッチします
PHFetchResult *assets = [PHAsset fetchAssetsInAssetCollection:myAlbum options:nil];

// フェッチ結果から assets を取り出します
NSArray * assetArray = [self getAssets:assets];

// asset を使って、UIImageView をランダムに更新します
[self updateImageViewWithAsset:assetArray[arc4random() % assets.count]];
}

- (PHAssetCollection *)getMyAlbum {
// ユーザ作成のアルバム一覧を指定して、PHAssetCollection をフェッチします
PHFetchResult *assetCollections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum
subtype:PHAssetCollectionSubtypeAlbumRegular
options:nil];

// [My Album]の AssetCollection を取得します
__block PHAssetCollection * myAlbum;
[assetCollections enumerateObjectsUsingBlock:^(PHAssetCollection *album, NSUInteger idx, BOOL *stop) {
if ([album.localizedTitle isEqualToString:@"My Album"]) {
myAlbum = album;
*stop = YES;
}
}];

return myAlbum;
}

- (NSArray *)getAssets:(PHFetchResult *)fetch {
// フェッチ結果を配列に格納します
__block NSMutableArray * assetArray = NSMutableArray.new;
[fetch enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) {
[assetArray addObject:asset];
}];
return assetArray;
}

- (void)updateImageViewWithAsset:(PHAsset *)asset {
typeof(self) __weak wself = self;
[[PHImageManager defaultManager] requestImageForAsset:asset
targetSize:CGSizeMake(300,300)
contentMode:PHImageContentModeAspectFill
options:nil
resultHandler:^(UIImage *result, NSDictionary *info) {
if (result) {
// imageVivew を更新します
wself.imageView.image = result;
}
}];
}


アルバムに画像を保存する

「写真.app」へ変更を要求する場合は、xxxChangeRequestというクラスを使用します。

保存するときに、identifierが取得できます。これを使って画像を取り出すこともできます。

- (void)addNewAssetWithImage:(UIImage *)image toAlbum:(PHAssetCollection *)album

{
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
// PHAssetChangeRequest を作ります
PHAssetChangeRequest *createAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image];

// PHAssetCollectionChangeRequest を作ります
PHAssetCollectionChangeRequest *albumChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:album];

// placeholder を取得して、PHAssetCollection に asset を追加します
PHObjectPlaceholder * placeHolder = [createAssetRequest placeholderForCreatedAsset];
[albumChangeRequest addAssets:@[ placeHolder ]];

// placeholder から取得できる identifier を
// 保存しておくと後から画像を一意に取得することができます
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:placeHolder.localIdentifier forKey:kAssetIdentifier];
[defaults synchronize];

} completionHandler:^(BOOL success, NSError *error) {
NSLog(@"Finished adding asset. %@", (success ? @"Success" : error));
}];
}


保存した画像をアルバムから Identifier で読み込む

getAssets:updateImageViewWithAsset:は上で記述したメソッドです。

- (void)readImageWidthIdentifier {

// Identifier を指定して PHAsset をフェッチします
NSString * identifier = [[NSUserDefaults standardUserDefaults]objectForKey:kAssetIdentifier];
PHFetchResult *assets = [PHAsset fetchAssetsWithLocalIdentifiers:@[identifier] options:nil];

// assets を取り出します
NSArray * assetArray = [self getAssets:assets];

// UIImageView を更新します
[self updateImageViewWithAsset:assetArray.lastObject];
}

ALAssestLibrary との違いとして、フェッチ結果の取り出しが同期になっているのが使いやすく感じました。

もっと使いこなせるようになっていきたいですね。


サンプルコード

https://github.com/taketomato/photosFWsample


参考URL

Photos Framework 公式ドキュメント

Developers.IO PhotoKit 特集