[iOS 8] PhotoKit 4 – Photos Framework – モデルオブジェクトのコンテンツの編集

はじめに

前回はモデルオブジェクトのプロパティの編集を扱いましたが、 今回はモデルオブジェクトのコンテンツ(写真やビデオの実データ)の編集を扱います。

PHAssetのコンテンツの編集手順

モデルオブジェクトのコンテンツの編集はPHAssetの場合のみ可能です。 PHAssetのコンテンツの編集手順について説明していきます。

- (void)applyFilter
{
    NSString *AdjustmentFormatIdentifier = @"com.example.samplecode.SamplePhotosApp";
    NSString *formatVersion = @"1.0";
    NSString *filterName = @"CISepiaTone";

    // [3] PHContentEditingInputRequestOptionsを作成、canHandleAdjustmentDataをセット
    PHContentEditingInputRequestOptions *options = [[PHContentEditingInputRequestOptions alloc] init];
    [options setCanHandleAdjustmentData:^BOOL(PHAdjustmentData *adjustmentData) {
        return [adjustmentData.formatIdentifier isEqualToString:AdjustmentFormatIdentifier] && [adjustmentData.formatVersion isEqualToString:formatVersion];
    }];

    // [1] 編集処理がサポートされているかを問い合わせる
    // 引数にPHAssetEditOperationContentを指定
    if ([self.asset canPerformEditOperation:PHAssetEditOperationContent]) {
        // [2] Assetのコンテンツ編集セッションを開始するために必要な情報をリクエスト
        [self.asset requestContentEditingInputWithOptions:options completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
            // [4] フルサイズのイメージを取得
            NSURL *url = [contentEditingInput fullSizeImageURL];
            int orientation = [contentEditingInput fullSizeImageOrientation];
            CIImage *inputImage = [CIImage imageWithContentsOfURL:url options:nil];
            inputImage = [inputImage imageByApplyingOrientation:orientation];

            // [5] フィルターを適用、NSDataを作成
            CIFilter *filter = [CIFilter filterWithName:filterName];
            [filter setDefaults];
            [filter setValue:inputImage forKey:kCIInputImageKey];
            CIImage *outputImage = [filter outputImage];
            NSData *jpegData = [outputImage aapl_jpegRepresentationWithCompressionQuality:0.9f];

            // [6] PHAdjustmentDataを作成
            PHAdjustmentData *adjustmentData = [[PHAdjustmentData alloc] initWithFormatIdentifier:AdjustmentFormatIdentifier formatVersion:formatVersion data:[filterName dataUsingEncoding:NSUTF8StringEncoding]];

            // [7] PHContentEditingOutputを作成、指定URLにjpegファイルを書き出し
            PHContentEditingOutput *contentEditingOutput = [[PHContentEditingOutput alloc] initWithContentEditingInput:contentEditingInput];
            [jpegData writeToURL:[contentEditingOutput renderedContentURL] atomically:YES];
            [contentEditingOutput setAdjustmentData:adjustmentData];

            // [8] Assetの編集を実行
            [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
                PHAssetChangeRequest *request = [PHAssetChangeRequest changeRequestForAsset:self.asset];
                request.contentEditingOutput = contentEditingOutput;
            } completionHandler:^(BOOL success, NSError *error) {
                if (!success) {
                    NSLog(@"%s Error: %@", __PRETTY_FUNCTION__, error);
                }
            }];
        }];
    }
}

[1] 編集処理がサポートされているかを問い合わせる

PHAssetのプロパティの編集を行う場合と同様に、 canPerformEditOperation:メソッドを使用して編集処理がサポートされているかをチェックします。

[2] Assetのコンテンツ編集セッションを開始するために必要な情報をリクエスト

次にPHAssetのrequestContentEditingInputWithOptions:completionHandler:メソッドを使用して PHAssetの編集セッションを開始するために必要な情報をリクエストします。

option引数には、[4]で作成するPHContentEditingInputRequestOptionsを渡します。

completionHandler引数に渡すblockは、編集用の情報が用意出来た時に呼ばれるので、 その時に行う処理を記述します。 blockの1つめの引数はPHContentEditingInputオブジェクトになっていて、 このオブジェクトを使用してコンテンツの編集に必要なデータ(Assetのプロパティやコンテンツ自体のURLなど)を取得できます。 2つめの引数のNSDictionaryにはリクエストのステートの状態が格納されます。

[3] PHContentEditingInputRequestOptionsを作成

PHContentEditingInputRequestOptionsのcanHandleAdjustmentDataプロパティにblockをセットしています。 このblockはPHAssetに対する以前の編集を続行できるかどうかを判断する必要がある時によばれます。

このblockでYESをした場合、編集用のオリジナルデータが提供され、 以前行なった編集を再現したり、加工を加えたりすることができます。

NOを返した場合は、以前行なった編集がレンダリングされたデータが提供されます。

PHAdjustmentDataについては[6]で説明します。

[4] フルサイズのイメージを取得

PHContentEditingInputのメソッドを使用して画像データを取得し、 CIImageオブジェクトを作成します。

[5] フィルターを適用、NSDataを作成

Core Imageのフィルターを適用し、NSDataを作成します。

[6] PHAdjustmentDataを作成

PHAdjustmentDataオブジェクトの作成を行います。 PHAdjustmentDataオブジェクトはAssetを編集したAppやExtensionが作成するオブジェクトで、 編集を再現するための「レシピ」になります。 PHAdjustmentDataオブジェクトのフォーマットはAppやExtension側で定義するもので、 例えば、フィルターアプリなら、ユーザーに選択されたフィルタやパラメータを保存しておきます。 あなたが定義したPHAdjustmentDataのフォーマットを知っているアプリであれば、 PHAdjustmentDataを使用して、編集をレジュームできます。

ここでは、フィルタ名の文字列を保存するようにしています。

[3]で「以前の編集を続行できるかどうかを判断する処理」について触れましたが、 [3]のcanHandleAdjustmentDataプロパティのblock内では、 ここで作成するPHAdjustmentDataをチェックして処理を分けています。

[7] PHContentEditingOutputを作成、指定URLにjpegファイルを書き出し

Assetのコンテンツの編集結果を表すオブジェクトであるPHContentEditingOutputオブジェクトを作成し、 PHContentEditingOutputオブジェクトのrenderedContentURLプロバティが示す場所にjpegファイルを書き出し、 [6]で作成したPHAdjustmentDataオブジェクトをセットしています。

[9] Assetの編集を実行

Assetのコンテンツの編集を実行します。 PHAssetのプロパティの編集を行う場合と同様に、 PHPhotoLibraryオブジェクトのperformChanges:completionHandler:メソッドを使用します。

1つ目の引数のblock内に編集処理を記述します。 PHAssetChangeRequestを作成し、contentEditingOutputプロパティに[8]で作成したPHContentEditingOutputオブジェクトをセットします。

2つめのblockに処理完了時の処理を記述します。

確認ダイアログ

Assetのプロパティの編集時には表示されありませんでしたが、コンテンツの編集の場合は 編集処理を実行したタイミングで以下のようなダイアログが表示されます。

ios8-photo-kit_edit-1

「変更」を選択した場合

「変更」 を選択すると、変更が実行されます。

ios8-photo-kit_edit-2

「許可しない」を選択した場合

「許可しない」 を選択すると、completionHandlerのblockの引数にNSErrorオブジェクトが渡されます。 (NSErrorオブジェクトのlocalizedDescriptionは「The operation couldn’t be completed. (Cocoa error -1.)」)

まとめ

今回はモデルオブジェクトのコンテンツ自体の編集について説明しました。プロパティの編集に比べて手順は多くなりますが、比較的簡単にコンテンツ自体を編集することができます。

次回はモデルオブジェクトの削除を扱います。

参考資料

本シリーズの記事一覧