自分で作ったビューの中にプレビューを表示するための方法を解説します。
Sponsor Link
使用するクラス
UIImagePickerController は、内部で色々とやってくれていたのですが、自分でビューをコントロールしようとすると、以下のようなクラスを使うことになります。
- AVCaptureSession
- AVFoundation を使う上でポイントとなるクラスです。このクラスに対して、入力や出力を設定することとなります。
- AVCaptureDevice/AVCaptureDeviceInput
- その名の通り、入力デバイスを表すクラスです。例えば、前面カメラだったり、背面カメラだったりです。
- AVCaptureOutput/AVCaptureDeviceOutput
- その名の通り、出力デバイスを表すクラスです。画像だったり、ビデオだったりを扱うことになります。
以下で、各クラスを少しづつ説明します。
AVCaptureSession
ビデオや写真を処理する上でポイントとなるクラスです。
Apple のドキュメントは、こちら。
入力デバイスから受け取ったデータを処理して、出力デバイスに渡すことを処理してくれます。
言い方を変えると、入力と出力を設定しないと何もしてくれません。
以下のような関数を使って、入出力を設定します。
- func addInput(_ input: AVCaptureInput)
- func addOutput(_ output: AVCaptureOutput)
上記以外に、canAddInput/canAddOutput で接続可能かを確認したり、removeInput/removeOutput で設定した入出力を止めることもできます。
AVCaptureDevice/AVCaptureDeviceInput
AVCaptureDevice がデバイスを表す抽象クラスで、AVCaptureDeviceInput は、入力デバイスを抽象化しています。
Appleのドキュメントは、こちら。
以下のような形で、AVCaptureDeviceInput を用意して、AVCaptureSesssion に設定します。
var captureSession: AVCaptureSession!
func setupSession() {
captureSession = AVCaptureSession()
captureSession.beginConfiguration()
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
guard let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice) else { return }
guard captureSession.canAddInput(videoInput) else { return }
captureSession.addInput(videoInput)
/* snip */
}
AVCaptureOutput/AVCaptureDeviceOutput
Apple のドキュメントは、こちら。
AVCaptureOutput がデバイスを表す抽象クラスで、AVCaptureDeviceOutput は、出力デバイスを抽象化しています。
以下のような形で、AVCaptureOutput を用意して、AVCaptureSession に設定します。
var captureSession: AVCaptureSession!
func setupSession() {
captureSession = AVCaptureSession()
captureSession.beginConfiguration()
/* snip */
let photoOutput = AVCapturePhotoOutput()
guard captureSession.canAddOutput(photoOutput) else { return }
captureSession.sessionPreset = .photo
captureSession.addOutput(photoOutput)
}
AVCaptureSession の設定まとめ
まとめると、以下のようなコードで、AVCaptureSession の入出力を設定することになります。
var captureSession: AVCaptureSession!
func setupSession() {
captureSession = AVCaptureSession()
captureSession.beginConfiguration()
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
guard let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice) else { return }
guard captureSession.canAddInput(videoInput) else { return }
captureSession.addInput(videoInput)
let photoOutput = AVCapturePhotoOutput()
guard captureSession.canAddOutput(photoOutput) else { return }
captureSession.sessionPreset = .photo
captureSession.addOutput(photoOutput)
captureSession.commitConfiguration()
}
ここまでのコードで基本的な処理はできるのですが、通常、プレビューが必要となります。
プレビューの方法
プレビューのために、AVCaptureVideoPreviewLayer というクラスが用意されています。そのなの通り、CALayer の子クラスです。
Apple のドキュメントは、こちら。
このクラスの初期化時に、AVCaptureSession を渡すと、そこで処理されているものをプレビューできるようになります。
AVCaptureVidePreviewLayer の生成
以下のようなコードで、対象となる AVCaptureSession を設定したAVCaptureVideoPreviewLayer を生成することができます。
let previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
self.layer.addSublayer(previewLayer) // # 表示させるためには、何らかのUIViewにレイヤーとして追加する必要があります。
AVCaptureSession の稼働
AVCaptureSession は、設定しただけでは動き始めません。.startRunning() をコールして、明示的に動作を開始させる必要があります。
SwiftUI で使うために、もう1手間必要
プレビューも含めて使うためには、CALayer を扱う必要があり、そのままでは、SwiftUI からは、直接使うことができません。
例によって、UIViewRepresentable で Wrap した UIView を使うことが必要となります。
public class UIAVCaptureVideoPreviewView: UIView {
var captureSession: AVCaptureSession!
func setupSession() {
captureSession = AVCaptureSession()
captureSession.beginConfiguration()
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
guard let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice) else { return }
guard captureSession.canAddInput(videoInput) else { return }
captureSession.addInput(videoInput)
let photoOutput = AVCapturePhotoOutput()
guard captureSession.canAddOutput(photoOutput) else { return }
captureSession.sessionPreset = .photo
captureSession.addOutput(photoOutput)
captureSession.commitConfiguration()
}
func setupPreview() {
self.frame = CGRect(x: 0, y: 0, width: 500, height: 500) // 空のUIViewを使っているため、適当なサイズ設定が必要です
let previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
previewLayer.frame = self.frame
self.layer.addSublayer(previewLayer)
self.captureSession.startRunning()
}
}
public struct SwiftUIAVCaptureVideoPreviewView: UIViewRepresentable {
public func makeUIView(context: Context) -> UIAVCaptureVideoPreviewView {
let view = UIAVCaptureVideoPreviewView()
view.setupSession()
view.setupPreview()
return view
}
public func updateUIView(_ uiView: UIAVCaptureVideoPreviewView, context: Context) {
}
}
この SwiftUIAVCaptureVideoPreviewView は、以下のように使います。
struct ContentView: View {
var body: some View {
VStack {
SwiftUIAVCaptureVideoPreviewView()
}
}
}
まとめ:動画/画像のプレビューを SwiftUI で表示するためには
以下の手順が必要となります。
- AVFoundation を使って、AVCaptureSession を設定する
- AVCaptureVideoPreviewLayer を使って、プレビューするためのビューを作る
- UIViewRepresentable を使って、プレビューするためのビューを SwiftUI から利用できるようにする
AVFoundation を使ったプレビューを 表示するための方法を説明しました。さらに、そのビューを SwiftUI から利用する方法も併せて説明しました。
説明は以上です。
Sponsor Link
[…] 前回の記事で説明しました。 […]