PriceRecordアプリでは、カメラでバーコードをスキャンする時に表示するプレビュー画面にAVCaptureVideoPreviewLayerを使用してる。
iPadを横向きに持った時は、何も設定しないとプレビュー画像も横向きになってしまうので、端末の向きに関わらず常に上向に表示させるため、プレビューを表示させる向きを設定する必要がある。
iOS16までの対応
端末の向きによってAVCaptureVideoPreviewLayerのconnection.videoOrientationに値を設定する。
端末の向き (ホームボタンの位置) | UIDeviceOrientation | AVCaptureVideoOrientation |
通常(下) | .portrait | .portrait |
横向き(右) | .landscapeLeft | .landscapeRight |
横向き(左) | .landscapeRight | .landscapeLeft |
上下逆向き(上) | .portraitUpsideDown | .portraitUpsideDown |
縦向きの場合は同じ名称、横向きの場合は反対の名称と覚えると覚えやすい。
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を設定するように変更になっているので対応は以下の通り。
端末の向き (ホームボタンの位置) | UIDeviceOrientation | videoRotationAngle |
通常(下) | .portrait | 90 |
横向き(右) | .landscapeLeft | 0 |
横向き(左) | .landscapeRight | 180 |
上下逆向き(上) | .portraitUpsideDown | 270 |
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)
}