📷

【iOS】AVFoundationを使用して写真撮影アプリを開発する

2021/07/16に公開


AVFoundationを使用して写真撮影アプリを開発する方法について書きます。写真撮影機能だけならば書くべきコードは至ってシンプルです。本記事のサンプルコードは以下に置いています。
https://github.com/NAOYA-MAEDA-DEV/AVFoundation-Camera-App

アクセス権限の確認

アプリからカメラやマイクといったiPhoneのデバイス、フォトライブラリや連絡先といったユーザデータへアクセスする時は、Info.plistにアクセスを行う項目とその理由を記載し、コード側でアクセス権限要求アラートを表示する処理を記述します。

Info.plistへの記載

Info.plistにアクセスを行う項目とその理由を記載します。写真撮影機能はiPhoneのカメラで撮影した写真をフォトライブラリに保存する為、必要なアクセス項目はカメラへのアクセス権限フォトライブラリへのアクセス権限です。

アクセス権限項目 Info.plistに記載する項目
カメラ Privacy - Camera Usage Description
フォトライブラリ Privacy - Photo Library Usage Description

アクセス権限要求アラートの表示

カメラへのアクセス権限

現在のカメラへのアクセス権限状態は以下のメソッドで確認することができます。

  • func authorizationStatus(for: AVMediaType) -> AVAuthorizationStatus

for に確認するアクセス権限のタイプを指定します。.video を指定するとカメラへのアクセス権限状態を確認することができます。

カメラへのアクセス権限要求アラートは以下のメソッドで表示することができます。

  • func requestAccess(for mediaType: AVMediaType, completionHandler handler: @escaping (Bool) -> Void)

for に要求するアクセス権限のタイプを指定します。.video を指定するとカメラへのアクセス権限要求アラートを表示することができます。handler にはアクセス権限確認後に行う処理を記述します。アクセスを許可した時はtrue 、許可しなかった時はfalse が引数として渡ってくるので、結果に応じて処理を分岐させることができます。
以下のコードではカメラへのアクセス権限状態をswitch 文の引数として、状態が.notDetermined つまり権限状態が未決定の時にアクセス権限要求アラートが表示されます。

フォトライブラリへのアクセス権限

現在のフォトライブラリへのアクセス権限状態は以下のメソッドで確認することができます。

  • func authorizationStatus(for: PHAccessLevel) -> PHAuthorizationStatus

for には確認したいフォトライブラリへのアクセスレベルを指定します。フォトライブラリの写真やビデオを読み込んだり、フォトライブラリへ写真やビデオを保存する時は.readWrite 、フォトライブラリに写真やビデオを保存するだけなら.addOnly を指定します。今回は撮影した写真を保存するだけなので.addOnly を指定します。

フォトライブラリへのアクセス権限要求アラートは以下のメソッドで表示することができます。

  • func requestAuthorization(for: PHAccessLevel, handler: (PHAuthorizationStatus) -> Void)

こちらもfor にはフォトライブラリへのアクセスレベルを指定します。今回は撮影した写真を保存するだけなので.addOnly を指定します。handler にはアクセス権限確認後に行う処理を記述します。
以下のコードではフォトライブラリへのアクセス権限状態をswitch 文の引数として、状態が.notDetermined つまり権限状態が未決定の時にアクセス権限要求アラートが表示されます。

キャプチャセッションの構築

キャプチャセッションとは入力ソースと出力タイプを繋いでくれる中継局のような役割を果たしてくれるクラスです。カメラの映像をiPhoneの画面に表示して、写真を撮影するためにはキャプチャセッションを構築する必要があります。キャプチャセッションは sessions(セッション)inputs(入力ソース)outputs(出力タイプ) をセットすることで構築することができます。

キャプチャセッションを構築する手順は以下の6ステップです。

  1. iPhoneのカメラデバイス(AVCaptureDevice )を取得
  2. AVCaptureDevice を使用してAVCaptureDeviceInput (入力ソース)を生成
  3. AVCaptureSession (セッション)にAVCaptureDeviceInput (入力ソース)をセット
  4. AVCaptureSession (セッション)にAVCapturePhotoOutput (出力タイプ)をセット
  5. AVCaptureSessionsessionPreset に表示する映像の品質をセット
  6. AVCaptureSession (セッション)からカメラ映像のプレビューレイヤー(AVCaptureVideoPreviewLayer)を取得してカメラ映像を表示したいViewにセット

手順を詳しく解説していきます。
AVCaptureDevice は入力デバイス(カメラ、マイク等)を抽象化したクラスです。AVCaptureDevice を使用してAVCaptureDeviceInput (入力ソース)を生成し、AVCaptureSession (セッション)にセットします。
AVCapturePhotoOutput は写真出力タイプを表すクラスです。AVCapturePhotoOutput を生成し、AVCaptureSession (セッション)にセットします。
sessionPreset は撮影する写真の品質を指定するプロパティです。AVCaptureSession.Preset型の要素をセットします。写真撮影を行う時はAVCaptureSession.Preset.photoをセットします。
最後にAVCaptureSession (セッション)カメラ映像のプレビューレイヤー(AVCaptureVideoPreviewLayer )を取得して、カメラ映像を表示したいUIViewにセットします。これでキャプチャセッションの構築は完了です。
最後にAVCaptureSessionstartRunning() を実行するとカメラの映像データがキャプチャセッションに流れ始めます。

以下のコードではiPhoneの広角レンズバックカメラ(AVCaptureDevice)を取得し、それを元に入力ソース(AVCaptureDeviceInput)を生成しています。AVCaptureSessionに対してその生成した入力ソースをセット、出力タイプは写真出力(AVCapturePhotoOutput)をセットし、カメラ映像のプレビューレイヤー(AVCaptureVideoPreviewLayer)をpreviewImageViewにセットしています。映像の品質は標準のカメラアプリで写真を撮影する時と同じ品質にする為のプリセット(AVCaptureSession.Preset.photo)をセットしています。

写真撮影の開始

AVCapturePhotoOutputcapturePhoto を実行すると写真撮影が開始します。capturePhoto の引数に写真撮影開始から完了するまでにコールされるデリゲートメソッドの委譲先と、撮影する写真のオプション情報を格納したAVCapturePhotoSettings オブジェクトを指定します。移譲先はself つまり以下のコードを記述したViewController を指定しています。AVCapturePhotoSettings オブジェクトに変更を加えることで写真を保存するフォーマットやメタデータを変更することができますが、写真を撮影するだけならば特に変更を加える必要はありません。

写真撮影で使用するデリゲートメソッド

写真撮影開始から終了までの間にいくつかのデリゲートメソッドがコールされますが、使用するデリゲートメソッドは以下の2つです。

  • func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?)
  • func photoOutput(_ output: AVCapturePhotoOutput, didFinishCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings, error: Error?)

前項でデリゲートの移譲先をselfに指定しました。上記デリゲートメソッドをViewController 内に記述する為に、移譲先のViewControllerAVCapturePhotoCaptureDelegate プロトコルに準拠する必要があります。

写真撮影のハンドリング処理

写真の出力完了

写真の出力が完了したタイミングでphotoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) がコールされます。引数のphoto は撮影した写真データです。photo の型はAVCapturePhoto で、このままではフォトライブラリに保存することができません。そこでfileDataRepresentation() を使用することでフォトライブラリに保存することができるData? 型に変換しています。写真を保存する時にこのData? 型のオブジェクトが必要になりますので変数で保持しておきます。

写真の保存

撮影に必要な一連の処理が全て完了したタイミングでphotoOutput(_ output: AVCapturePhotoOutput, didFinishCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings, error: Error?)がコールされます。photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) 内で保持しておいたData? 型のオブジェクトを写真データとして、PHPhotoLibrary.shared().performChanges を使用してフォトライブラリに保存します。

以上で写真撮影機能の実装は完了です。次回はビデオ撮影機能について解説します。

Discussion