🤳
【Swift5】デバイスを回転させるとカメラ(AVCaptureVideoPreviewLayer)の向きがおかしくなる現象の解決方法
何が発生しているのか
AVCaptureVideoPreviewLayer
でカメラを使うと、デバイスが回転した際、カメラが表示しているのがデバイスの向きと一致しない。
以下のコードはSwiftでカメラアプリを作成する(1)に追記する形になります。
対策
レイヤーの向き
1.画面の向きを簡単に表すenumを作成
ViewController.swift
/// 画面の向き
enum Orientaion: Int {
/// ホームボタンの位置が
case right = -1,
down = 0,
left = 1,
up = 2
}
2.画面の向きを取得する関数
ViewController.swift
/// 画面の向きからオリエンテーションを取得する
private func getDeviceOrientation() -> Orientaion {
guard let interfaceOrientation = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.windowScene?.interfaceOrientation else {
return .right
}
switch interfaceOrientation {
case .landscapeLeft:
return .left
case .landscapeRight:
return .right
case .portrait:
return .down
case .portraitUpsideDown:
return .up
default:
return .right
}
}
3.カメラの向きを修正する関数
ViewController.swift
/// カメラの向きを修正する
private func fixCameraOrientation() {
// ロード時からいくら回転したかを取得
let rotation: CGFloat = CGFloat(getDeviceOrientation().rawValue) * 90
// レイヤーなのでCATransform3DMakeRotationを使って回転させます
self.cameraPreviewLayer?.transform = CATransform3DMakeRotation(
rotation / 180 * CGFloat.pi,
0,
0,
1
)
// 回転してもフレームは前のままなので、親ビューと合わせてあげます
cameraPreviewLayer?.frame = view.frame
}
/// 画面の向きについてセットアップする
private func setUpOrientaion() {
// 通常の状態との違いの角度を取得
let rotation: CGFloat = {
switch initialOrientation {
case .down:
return 0
case .right:
return -90
case .up:
return 180
case .left:
return 90
}
}()
self.cameraPreviewLayer?.transform = CATransform3DMakeRotation(
rotation / 180 * CGFloat.pi,
0,
0,
1
)
// フレームを調整
cameraPreviewLayer?.frame = view.frame
}
4.画面ロード時と画面回転時に修正をする
ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
//カメラの向きを設定
setUpOrientaion()
}
//画面回転時の動作
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(
alongsideTransition: { _ in
//カメラの向きを修正
self.fixCameraOrientation()
}
)
}
写真の向き
レイヤーの向きと一緒に、写真の向きもおかしくなるので修正します
ViewController.swift
//MARK: AVCapturePhotoCaptureDelegateデリゲートメソッド
extension ViewController: AVCapturePhotoCaptureDelegate {
// 撮影した画像データが生成されたときに呼び出されるデリゲートメソッド
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
//画像のデータを取得し、
if let imageData = photo.fileDataRepresentation() {
//画像の向きを判定し、修正する向きを設定
let imageOrientation: UIImage.Orientation = {
switch getDeviceOrientation(){
case .down:
return .right
case .right:
return .up
case .left:
return .down
case .up:
return .left
}
}()
//向きを考慮して画像に変換
let image = UIImage(cgImage: UIImage(data: imageData)!.cgImage!,
scale: 1.0,
orientation: imageOrientation
)!
//できた画像で色々な処理
doSomething(image)
}
}
}
これで撮った写真も正常な向きになります。
完了
以上で、カメラの表示がおかしくなるのは一応修正出たと思いますが、画面回転時のレイヤーの動きが少し挙動不審なのが気になります...
間違えているところがあったり、もっといい方法があったら教えてください。
参考文献
-
Swiftでカメラアプリを作成する(1)
https://qiita.com/t_okkan/items/f2ba9b7009b49fc2e30a
カメラのアプリを作るのに使いました -
iOSでアニメーションを行う方法”回転”
https://www.indetail.co.jp/blog/ios-animation-rotation/
CATransform3DMakeRotationの使い方 -
CALayerで3Dグラフィックス?
https://terazzo.hatenadiary.org/entry/20111207/1323283155
同上
Discussion