iOS9までは別のクラス使ってたんだけど、iOS10からは非推奨になったので、これを使わにゃいかんのだ。
必要なFrameworkはAVFoundation。
以下に全コードを載せる。
撮影ボタンをUIButtonじゃなくて上部のツールバーにしてるけど、まあいいっしょ?
コードをシンプルにして構造をわかりやすくするため、極力エラー処理は省いてある。
カメラの使用とフォトライブラリへのアクセスをするので、あらかじめInfo.plistにそれに関するプライバシーの警告テキストを入力しておくこと。んでないと落ちる。
AVCaptureSessionクラス に入出力の情報をいろいろ設定し、startRunning() でカメラのライブビュー(プレビュー)表示を開始。ライブビュー映像はLayerを指定しないといけないようなので、imageViewにLayerを追加して表示している。
撮影ボタンを押すと takePhoto() が呼ばれ撮影。
撮影された画像はdelegateメソッドである capture(〜)メソッド の引数として受け取れるので、そこでDataからUIImageに変換。
それを普通に UIImageWriteToSavedPhotosAlbum()メソッド で写真ライブラリに保存している。
シミュレータじゃカメラ機能が使えないからiPadの実機で試したんだけど、撮影時のflashModeの行をコメントアウトしないと落ちてしまう。
iPadにはストロボないけど、.autoにしとけば自動で判断してくれそうなものだが。
コード
import UIKit
import AVFoundation
//キャプチャの入出力管理クラス
var captureSession:AVCaptureSession?
//静止画データの出力用
var stillImageOutput:AVCapturePhotoOutput?
//キャプチャ画像のプレビュー用
var videoPreviewLayer:AVCaptureVideoPreviewLayer?
//プレビュー表示用
var imageView = UIImageView()
class ViewController: UIViewController, AVCapturePhotoCaptureDelegate {
override func viewDidLoad() {
super.viewDidLoad()
//ツールバーに撮影ボタン追加
let toolBar = UIToolbar(frame: CGRect(x: 0, y: 10, width: view.bounds.size.width, height: 44))
let takePhotoButton = UIBarButtonItem(title: "撮影", style: .plain, target: self, action: #selector(ViewController.takePhoto))
toolBar.items = [takePhotoButton]
view.addSubview(toolBar)
//プレビューに使うimageViewを作成
imageView = UIImageView(frame: CGRect(x: 0, y: toolBar.frame.size.height, width: view.bounds.size.width, height: view.bounds.size.height))
self.view.addSubview(imageView)
//カメラの画像をキャプチャするクラスを作る
captureSession = AVCaptureSession()
//撮影解像度設定
captureSession?.sessionPreset = AVCaptureSessionPresetPhoto
//キャプチャーデバイスをビデオカメラに設定
let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
//ビデオを入力する。try!なのでエラー処理をせず、入力エラーの場合クラッシュ
let input = try! AVCaptureDeviceInput(device: device)
//キャプチャー管理のクラスに入力情報追加
captureSession?.addInput(input)
//キャプチャー管理のクラスに出力情報追加
stillImageOutput = AVCapturePhotoOutput()
captureSession?.addOutput(stillImageOutput)
//プレビューレイヤー作成
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
//プレビューレイヤーのframe設定
videoPreviewLayer?.frame = imageView.bounds
//UIImageViewのContentModeに相当
videoPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
//Layerのbounds部分以外をマスクする
videoPreviewLayer?.masksToBounds = true
//imageViewのlayerとしてプレビューレイヤーを乗せる
imageView.layer.addSublayer(videoPreviewLayer!)
//セッション開始
captureSession?.startRunning()
}
func takePhoto() {
let settingsForMonitoring = AVCapturePhotoSettings()
//カメラのストロボモード
// settingsForMonitoring.flashMode = .auto //コメントにしないと落ちる
//カメラの手ぶれ補正
settingsForMonitoring.isAutoStillImageStabilizationEnabled = true
//最高解像度で撮影するか否か
settingsForMonitoring.isHighResolutionPhotoEnabled = false
//画面キャプチャ処理
//撮影された画像はdelegateメソッドで処理する
stillImageOutput?.capturePhoto(with: settingsForMonitoring, delegate: self)
}
//AVCapturePhotoCaptureDelegateメソッド
func capture(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) {
//結果がnilじゃなければ
if let photoSampleBuffer = photoSampleBuffer {
//キャプチャされたphotoSampleBufferの画像をJPEGとしてDataに直す
//previewPhotoはサムネイル画像らしい
let photoData = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: photoSampleBuffer, previewPhotoSampleBuffer: previewPhotoSampleBuffer)
//DataをUIImageに変換
let image = UIImage(data: photoData!)
//写真ライブラリに保存
UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil)
}
}
}
エラーチェックをする場合
で挟まれたところを置き換える。
//キャプチャするデバイスをビデオに設定
guard let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) else {
print("デバイスエラー")
return
}
//ビデオを入力する。try!なのでエラー処理をせず、入力エラーの場合クラッシュ
let input = try! AVCaptureDeviceInput(device: device)
//追加可能な入力かどうか調べる
if captureSession!.canAddInput(input) {
//キャプチャー管理のクラスに入力情報追加
captureSession?.addInput(input)
//静止画の出力クラスを設定
stillImageOutput = AVCapturePhotoOutput()
//追加可能な出力かどうか調べる
if captureSession!.canAddOutput(stillImageOutput) {
//キャプチャー管理のクラスに出力情報追加
captureSession?.addOutput(stillImageOutput)
//プレビューレイヤー作成
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
//guardは、nilでなければunwrapして返し、nilならそこで終る
guard let _videoPreviewLayer = videoPreviewLayer else {
return
}
//プレビューレイヤーのframe設定
_videoPreviewLayer.frame = imageView.bounds
//UIImageViewで言うところのContentModeのこと
_videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
//Layerのbounds部分以外をマスクする
_videoPreviewLayer.masksToBounds = true
//imageViewのlayerとしてプレビューレイヤーを乗せる
imageView.layer.addSublayer(_videoPreviewLayer)
//セッション開始
captureSession?.startRunning()
} else {
print("出力先エラー")
}
} else {
print("入力元エラー")
}
参考
0 件のコメント:
コメントを投稿