11import AVFoundation
2+ import Combine
23import UIKit
34import Vision
45
@@ -36,6 +37,8 @@ final class CodeScannerViewController: UIViewController {
3637 private let sessionQueue = DispatchQueue ( label: " qrlogincamerasession.queue.serial " )
3738 private let session = AVCaptureSession ( )
3839 private var requests = [ VNRequest] ( )
40+ private var rotationCoordinator : AVCaptureDevice . RotationCoordinator ?
41+ private var cancellables = Set < AnyCancellable > ( )
3942
4043 private lazy var throttler : Throttler = Throttler ( seconds: 0.1 )
4144
@@ -160,10 +163,8 @@ private extension CodeScannerViewController {
160163 /// Enables and starts live stream video, if available.
161164 func startLiveVideo( ) {
162165 session. sessionPreset = . photo
163- if #available( iOS 16 . 0 , * ) {
164- if session. isMultitaskingCameraAccessSupported {
165- session. isMultitaskingCameraAccessEnabled = true
166- }
166+ if session. isMultitaskingCameraAccessSupported {
167+ session. isMultitaskingCameraAccessEnabled = true
167168 }
168169
169170 guard let captureDevice = AVCaptureDevice . default ( for: . video) ,
@@ -187,6 +188,15 @@ private extension CodeScannerViewController {
187188 previewLayer. videoGravity = . resizeAspectFill
188189 previewLayer. frame = videoOutputImageView. bounds
189190 videoOutputImageView. layer. addSublayer ( previewLayer)
191+ rotationCoordinator = AVCaptureDevice . RotationCoordinator ( device: captureDevice, previewLayer: previewLayer)
192+
193+ rotationCoordinator? . publisher ( for: \. videoRotationAngleForHorizonLevelPreview)
194+ . sink { [ weak self] angle in
195+ guard let self = self ,
196+ let connection = self . previewLayer? . connection else { return }
197+ connection. videoRotationAngle = angle
198+ }
199+ . store ( in: & cancellables)
190200
191201 sessionQueue. async { [ weak self] in
192202 self ? . session. startRunning ( )
@@ -289,28 +299,35 @@ private extension CodeScannerViewController {
289299// MARK: Orientation Handling
290300//
291301private extension CodeScannerViewController {
302+ enum VideoRotationAngle {
303+ static let portrait : CGFloat = 90
304+ static let landscapeRight : CGFloat = 0
305+ static let landscapeLeft : CGFloat = 180
306+ static let portraitUpsideDown : CGFloat = 270
307+ }
308+
292309 func updatePreviewLayerOrientation( ) {
293- if let connection = previewLayer? . connection, connection . isVideoOrientationSupported {
310+ if let connection = previewLayer? . connection {
294311 let orientation = view. window? . windowScene? . interfaceOrientation
295- let videoOrientation : AVCaptureVideoOrientation
312+ let videoRotationAngle : CGFloat
296313 switch orientation {
297314 case . portrait:
298- videoOrientation = . portrait
315+ videoRotationAngle = VideoRotationAngle . portrait
299316 case . landscapeRight:
300- videoOrientation = . landscapeRight
317+ videoRotationAngle = VideoRotationAngle . landscapeRight
301318 case . landscapeLeft:
302- videoOrientation = . landscapeLeft
319+ videoRotationAngle = VideoRotationAngle . landscapeLeft
303320 case . portraitUpsideDown:
304- videoOrientation = . portraitUpsideDown
321+ videoRotationAngle = VideoRotationAngle . portraitUpsideDown
305322 default :
306- videoOrientation = . portrait
323+ videoRotationAngle = VideoRotationAngle . portrait
307324 }
308- updatePreviewLayerVideoOrientation ( connection: connection, orientation : videoOrientation )
325+ updatePreviewLayerVideoRotationAngle ( connection: connection, angle : videoRotationAngle )
309326 }
310327 }
311328
312- func updatePreviewLayerVideoOrientation ( connection: AVCaptureConnection , orientation : AVCaptureVideoOrientation ) {
313- connection. videoOrientation = orientation
329+ func updatePreviewLayerVideoRotationAngle ( connection: AVCaptureConnection , angle : CGFloat ) {
330+ connection. videoRotationAngle = angle
314331 }
315332}
316333
0 commit comments