Swiftで写真を撮影して保存するiPhoneアプリを作成
iPhoneのカメラ機能を利用する入門的なアプリをサクッと作る。 フレームワークの各関数の使い方をよく忘れるのでメモ代わりに記録として残しておく。
画面
画面は1つのみで、プレビュー用のView(cameraView)とシャッター用のButton(take)の2つの部品だけを配置。 下図ではViewを正方形に配置しているが、実際に保存されるアスペクト比に近い形の方が良いとは思う。
コード
以下のコードを実装して、iPhone繋いでBuild&Runして、takeボタン押せば、 プレビュー画面が写真としてカメラロールに保存される。 コードの解説については、コメントを参照。
import UIKit import AVFoundation class FirstViewController: UIViewController, AVCapturePhotoCaptureDelegate { // カメラ等のキャプチャに関連する入出力を管理するクラス var session: AVCaptureSession! // 写真データを取得するクラス var outout: AVCapturePhotoOutput? // カメラでキャプチャした映像をプレビューするクラス var previewLayer: AVCaptureVideoPreviewLayer? // カメラでキャプチャした映像をプレビューするエリア @IBOutlet weak var cameraView: UIView! // ロードされた直後に呼び出される(初回に一度のみ) override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. } // 画面が表示される直前に呼び出される override func viewWillAppear(_ animated: Bool) { // キャプチャー入出力と写真データ取得のクラスの初期化 session = AVCaptureSession() outout = AVCapturePhotoOutput() // 解像度の設定 //session.sessionPreset = AVCaptureSessionPreset1920x1080 session.sessionPreset = AVCaptureSessionPreset3840x2160 // カメラの選択(背面・前面など) let camera = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) do { // 指定したデバイスをセッションに入力 let input = try AVCaptureDeviceInput(device: camera) // 入力 if (session.canAddInput(input)) { session.addInput(input) // 出力 if (session.canAddOutput(outout)) { session.addOutput(outout) session.startRunning() // カメラ起動 // プレビューレイヤーを生成 previewLayer = AVCaptureVideoPreviewLayer(session: session) // cameraViewの境界をプレビューレイヤーのフレームに設定 previewLayer?.frame = cameraView.bounds // アスペクト比変更とレイヤー収納の有無 previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill // アスペクト比を変ない。レイヤーからはみ出した部分は隠す。 //previewLayer?.videoGravity = AVLayerVideoGravityResizeAspect // アスペクト比を変えない。はみ出さないようにレイヤー内に収める。 // cameraViewのサブレイヤーにプレビューレイヤーを追加 cameraView.layer.addSublayer(previewLayer!) } } } catch { print(error) } } // ボタンをタップした時に呼ばれる @IBAction func takePhoto(_ sender: Any) { // 撮影設定 let settingsForMonitoring = AVCapturePhotoSettings() settingsForMonitoring.flashMode = .auto // フラッシュのモード settingsForMonitoring.isAutoStillImageStabilizationEnabled = true // 手振れ補正 settingsForMonitoring.isHighResolutionPhotoEnabled = false // 最高解像度で撮影するか否か // シャッターを切る outout?.capturePhoto(with: settingsForMonitoring, delegate: self) } // AVCapturePhotoCaptureDelegateのデリゲート // カメラで撮影が完了した後呼ばれる。撮影データを加工したり、アルバムに保存したりする。 func capture(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) { if let photoSampleBuffer = photoSampleBuffer { // JPEG形式で画像データを取得 let photoData = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: photoSampleBuffer, previewPhotoSampleBuffer: previewPhotoSampleBuffer) // UIImage型に変換 let image = UIImage(data: photoData!) // フォトライブラリに保存 UIImageWriteToSavedPhotosAlbum(image!, nil, nil, nil) } } // 警告を受け取ったときに呼ばれる override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
トピックス
解像度の設定
// 解像度の設定 //session.sessionPreset = AVCaptureSessionPreset1920x1080 session.sessionPreset = AVCaptureSessionPreset3840x2160
もう少し写真の解像度を上げたいなどの場合は、ここで設定を行う。 指定できる解像度の種類は以下の通り。
Symbol | Description |
---|---|
AVCaptureSessionPresetPhoto | 高解像度の写真品質出力 |
AVCaptureSessionPresetHigh | 高品質のビデオおよびオーディオ出力 |
AVCaptureSessionPresetMedium | WiFi経由での共有出力ビデオおよびオーディオビットレート |
AVCaptureSessionPresetLow | 3G経由での共有出力ビデオおよびオーディオビットレート |
AVCaptureSessionPreset320x240 | 320x240ピクセルビデオ出力 |
AVCaptureSessionPreset352x288 | CIF画質(352x288ピクセル)ビデオ出力 |
AVCaptureSessionPreset640x480 | VGA画質(640x480ピクセル)ビデオ出力 |
AVCaptureSessionPreset960x540 | クオータHD品質(960x540ピクセル)ビデオ出力 |
AVCaptureSessionPreset1280x720 | 720p画質(1280x720ピクセル)のビデオ出力 |
AVCaptureSessionPreset1920x1080 | 1080p品質(1920x1080ピクセル)ビデオ出力 |
AVCaptureSessionPreset3840x2160 | 2160p(UHDまたは4Kとも呼ばれる)画質(3840x2160ピクセル)ビデオ出力 |
AVCaptureSessionPresetiFrame960x540 | AACオーディオで960x540の高品質iFrame H.264ビデオを約30Mbits/secで実現する設定 |
AVCaptureSessionPresetiFrame1280x720 | 1280x720の高品質iFrame H.264ビデオをAACオーディオで約40 Mbits/secで実現する設定 |
AVCaptureSessionPresetInputPriority | キャプチャセッションがオーディオおよびビデオ出力設定を制御しないことを指定 |
[iOS] AVFundationを使用して、「ビデオ録画」や「連写カメラ」や「QRコードリーダー」や「バーコードリーダー」を作ってみた - Developers.IO より
アスペクト比変更とレイヤー収納の有無
// アスペクト比変更とレイヤー収納の有無 previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill // アスペクト比を変ない。レイヤーからはみ出した部分は隠す。 //previewLayer?.videoGravity = AVLayerVideoGravityResizeAspect // アスペクト比を変えない。はみ出さないようにレイヤー内に収める。
プレビューの変更を行う。上記の設定ではcameraViewに実際に撮影される写真の一部しかプレビューで表示されないが、 設定によっては全部表示させることも可能。以下の3種類がある。
Symbol | Description |
---|---|
AVLayerVideoGravityResize | アスペクト比を変えてレイヤーに収める。 |
AVLayerVideoGravityResizeAspect | アスペクト比を変えない。はみ出さないようにレイヤー内に収める。 |
AVLayerVideoGravityResizeAspectFill | アスペクト比を変ない。レイヤーからはみ出した部分は隠す。 |
AVCaptureVideoPreviewLayer の Video Gravity3種 - 木木木 より
参考文献
- AVFoundationのキャプチャ機能について - tomoyaonishiのブログ
- [iOS 10] iOS 10以降のAVFoundationでの撮影方法 - Developers.IO
- AppDelegate,UIViewController,UIViewのライフサイクル/iOS/Swift - Qiita
- [iOS] AVFundationを使用して、「ビデオ録画」や「連写カメラ」や「QRコードリーダー」や「バーコードリーダー」を作ってみた - Developers.IO
- カメラの利用(AVFoundation)(Swift) - モバイル開発系(K)
- AVCaptureVideoPreviewLayer の Video Gravity3種 - 木木木
- プロトコルとデリゲートのとても簡単なサンプルについて - Qiita
- Xcode:didReceiveMemoryWarning() メソッドとは? - オープンデータとプログラミング