ビデオプレビューの方向設定について

PriceRecordアプリでは、カメラでバーコードをスキャンする時に表示するプレビュー画面にAVCaptureVideoPreviewLayerを使用してる。

iPadを横向きに持った時は、何も設定しないとプレビュー画像も横向きになってしまうので、端末の向きに関わらず常に上向に表示させるため、プレビューを表示させる向きを設定する必要がある。

iOS16までの対応

端末の向きによってAVCaptureVideoPreviewLayerのconnection.videoOrientationに値を設定する。

端末の向き
(ホームボタンの位置)
UIDeviceOrientationAVCaptureVideoOrientation
通常(下).portrait.portrait
横向き(右).landscapeLeft.landscapeRight
横向き(左).landscapeRight.landscapeLeft
上下逆向き(上).portraitUpsideDown.portraitUpsideDown
iOS16までの設定

縦向きの場合は同じ名称、横向きの場合は反対の名称と覚えると覚えやすい。

private func updateVideoOrientation() {
        guard let connection = previewLayer?.connection else { return }
        switch UIDevice.current.orientation {
        case .portrait:
            connection.videoOrientation = .portrait
        case .landscapeLeft:
            connection.videoOrientation = .landscapeRight
        case .landscapeRight:
            connection.videoOrientation = .landscapeLeft
        case .portraitUpsideDown:
            connection.videoOrientation = .portraitUpsideDown
        default:
            break
        }
}

iOS17からの対応

iOS17からはvideoRotationAngleを設定するように変更になっているので対応は以下の通り。

端末の向き
(ホームボタンの位置)
UIDeviceOrientationvideoRotationAngle
通常(下).portrait90
横向き(右).landscapeLeft0
横向き(左).landscapeRight180
上下逆向き(上).portraitUpsideDown270
iOS17からの設定

videoRotationAngleは、現在は0,90,180,270の4つの値に対応しているが、名前が定義されていないので直接値を設定する必要がある。

将来的に30や45など斜めになる事を想定しているかどうかは不明。

private func updateVideoAngle() {
        guard let connection = previewLayer?.connection else { return }
        switch UIDevice.current.orientation {
        case .portrait:
            connection.videoRotationAngle = 90
        case .landscapeLeft:
            connection.videoRotationAngle = 0
        case .landscapeRight:
            connection.videoRotationAngle = 180
        case .portraitUpsideDown:
            connection.videoRotationAngle = 270
        default:
            break
        }
}

値が4つしかないので、配列を定義しておいて、端末の向きからindexを求めて90を掛けて計算する方法も有りかと…

let orientations : [UIDeviceOrientation] = [.landscapeLeft, .portrait, .landscapeRight, .portraitUpsideDown]

if let index = orientations.firstIndex(of: UIDevice.current.orientation) {
    connection.videoRotationAngle = CGFloat(index * 90)
}