*

block構文とクロージャとcapture StillImage Asynchronously From Connection – コタツと台所にて作るアプリ 11

      2015/11/01

SUN55_cyairoiblockbei20131116001500

「お。カメラ画面の表示ができた。」

土台となるObjective-CのソースをSwiftに書き換え始めてから1時間程度でカメラ画面の表示までができた。

(これは山場と思ってたけど、このペースなら意外とあっさりいけるかもしれない。)

私は実機テストとして使っている自分のiPhone6に映るカメラ画面を見ながら、そんなことを思った。

本当にその通りであれば、時間がない自分にとっては開発工数が短縮できるまたとないチャンスだ。

しかしその甘い考えは、すぐに崩れ去ることとなる。

カメラ画面の表示までできたのなら、デバイスの切替機能などは置いておいて、まずは保存の実装が先である。

サンプルソースから保存機能のメソッドを探そうと、2本指でトラックパッドを操作する。

そして、その指が止まった。

「ブ、ブロック構文・・・」

本当の山場はここからだった。

ブロック構文とクロージャ

Objective-Cにはブロック構文というものがあります。

このブロック構文、私は全然理解が及んでいない構文です。

ですので、詳しいことが書かれている以下のサイト様などを見て理解をしていきました。

【iPhoneアプリ】これは便利かも!?「block構文」について

ブロック構文の基本的な使い方

「無名関数」「ラムダ式」などの単語もありますが、その場限りのメソッドと捉えたほうがよさそうですね。

そして、このblock構文。Swiftではどう変わっているかというと、クロージャという形になったようです。

そのあたりも以下のサイト様に詳しくのっています。

[Swift] クロージャ(Closures)のサンプル

関数 func とクロージャ{ in }

[iOS] Swiftのクロージャを使う

今の問題は写真の保存

クロージャをうまく使いこなせば、かなり短縮されたソースコードも書けるようです。

ただ、この時の私はそれをゆっくりと会得する時間的余裕はありません。

そのため、block構文で書かれている写真を保存する箇所の処理をSwiftで置き換えることに注力しました。

まず、サンプルソースとしたBasicCameraAppsの写真保存部分のところです。

-(void)takePhoto:(takePhotoBlock) block {
    AVCaptureConnection* connection = [imageOutput connectionWithMediaType:AVMediaTypeVideo];
    
    // 画像の向きを調整する
    if (connection.isVideoOrientationSupported) {
        // 明示的に
        connection.videoOrientation = (AVCaptureVideoOrientation)[[UIDevice currentDevice] orientation];
    }
    
    // UIImage化した画像を通知する
    [imageOutput captureStillImageAsynchronouslyFromConnection:connection
                                             completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
                                                 if(imageDataSampleBuffer == nil){
                                                     block(nil,error);
                                                     return;
                                                 }
                                                 
                                                 NSData *data = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
                                                 UIImage *image = [UIImage imageWithData:data];
                                                 
                                                 block(image,error);
                                             }
     ];
}

takePhotoメソッドの後にblock構文があります。

そして、その中で処理を記述していて、さらにcaptureStillImageAsynchronouslyFromConnectionメソッドのblock構文の中で、OutputからのデータをUIImage化しています。

AVFoundationを使ったカメラの解説でもありますが、captureStillImageAsynchronouslyFromConnectionメソッドは非同期で行われるようで、block内の処理はメソッド処理終了後に行われる処理のようです。

ですので、UIImage化をしている部分も、captureStillImageAsynchronouslyFromConnectionメソッドが非同期で処理され、それが終わった後に実行されるので、こちらも非同期処理ということになります。

つづいて、takePhotoのメソッド自体を見ていきます。

このtakePhotoは以下のような形で呼び出されています。

[_captureManager takePhoto:^(UIImage *image, NSError *error) {
            // 静止画保存ここでやってる
            _captureview.image = image;
            [self saveCaptureFile:image];
        }];

このblock構文の役割もdelegeteと同じようで、captureStillImageAsynchronouslyFromConnectionの処理が終わった後に処理を行いたいので、block構文としてメソッドを定義し、呼び出し元でtakePhoto内部の処理後に行いたい処理を書いていくような形のようです。

目下、この2つのblock構文について、クロージャで書いていきたいと思います。

乗り越えるべき山

私が、実際のSwiftでどう書いたかは次の記事で書きたいと思います。

ちなみにこの記事で書いたことを理解するまで結構かかりました。(実際理解として正しいか疑問に思っているところはありますが。)

時間がない中、予定外の事態だったので、焦ってなかなか頭に入らなかったんです。

ただ、クロージャは今後も使うでしょうし、block構文もいろんなソースを参考にする中で、度々出てくるものなので、ここで覚えておいたほうが徳だ。これも乗り越える必要があるんだと思い直してからは、割とすんなりと頭に入ってきました。

何事も焦りは禁物ということでしょうか。

 - objective-c, Swift, Xcode, アプリ