user924
user924

Reputation: 12123

How to correctly use RotationCoordiantor and its videoRotationAngleForHorizonLevelPreview to set camera preview orientation

So I'm trying to set camera orientation for AVCaptureVideoPreviewLayer in SwiftUI without deprecated methods (AVCaptureConnection.videoOrientation is deprecated now) but it doesn't work correct:

struct CameraPreview: UIViewRepresentable {
    let previewLayer: AVCaptureVideoPreviewLayer
    let rotationCoordiantor: AVCaptureDevice.RotationCoordinator

    func makeUIView(context: Context) -> CameraPreviewView {
        let view = CameraPreviewView(previewLayer: previewLayer, rotationCoordiantor: rotationCoordiantor)
        return view
    }

    func updateUIView(_ uiView: CameraPreviewView, context: Context) {
        // Update the preview layer's frame whenever the view's size changes
        uiView.updatePreviewLayerFrame()
    }
}

class CameraPreviewView: UIView {
    private let previewLayer: AVCaptureVideoPreviewLayer
    private let rotationCoordiantor: AVCaptureDevice.RotationCoordinator

    init(previewLayer: AVCaptureVideoPreviewLayer, rotationCoordiantor: AVCaptureDevice.RotationCoordinator) {
        self.previewLayer = previewLayer
        self.rotationCoordiantor = rotationCoordiantor
        super.init(frame: .zero)
        self.layer.addSublayer(previewLayer)
        // actually we don't need to observe device orientation, but just for test
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(orientationDidChange),
            name: UIDevice.orientationDidChangeNotification,
            object: nil
        )
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        updatePreviewLayerFrame()
    }
    
    @objc private func orientationDidChange() {
        updatePreviewLayerFrame()
    }
    
    func updatePreviewLayerFrame() {
        previewLayer.frame = self.bounds
        
        print("angle \(rotationCoordiantor.videoRotationAngleForHorizonLevelPreview)")
        
        previewLayer.connection?.videoRotationAngle = rotationCoordiantor.videoRotationAngleForHorizonLevelPreview
        
        Task {
            try await Task.sleep(nanoseconds: 1000000000) // delay one second
            print("angle \(rotationCoordiantor.videoRotationAngleForHorizonLevelPreview)")
            // I get different values here after a second without changing phone orientation, 
            // so rotationCoordiantor is not synced fast enough with device orientation
        }
    }
}

When I previously used previewLayer?.connection.videoOrientation = ... instead of previewLayer.connection?.videoRotationAngle = ...it worked fine

p.s. RotationCoordiantor instance was created in ViewModel like this:

rotationCoordiantor = AVCaptureDevice.RotationCoordinator(
    device: camera,
    previewLayer: previewLayer
)

Upvotes: 2

Views: 132

Answers (0)

Related Questions