I am using CameraPreview
(UIViewRepresentable
) as a bridge between UIKit
and SwiftUI
. CameraPreview
is configured with CameraModel
and uses AVCaptureVideoPreviewLayer
. CameraModel
is configuring AVCaptureSession
, and AVCaptureDeviceInput
. Everything works fine, except when I try to rotate device on iPad. The CameraPreview
is not rotated properly and uses input from previous rotation. (Pictures below) I can check the device being rotated in SwiftUI (based on this article) , but I do not know how to reconfigure rotation ofCameraPreview
and AVCaptureVideoPreviewLayer
. Any Idea how to trigger layout update in UIKit
after I find out that device was rotated?
Code below:
struct CameraView: View {
@StateObject var camera = CameraModel()
var body: some View {
CameraPreview(camera: camera)
.ignoresSafeArea(.all, edges: .all)
}
}
struct CameraPreview: UIViewRepresentable {
@ObservedObject var camera: CameraModel
func makeUIView(context: Context) -> UIView {
let view = UIView(frame: UIScreen.main.bounds)
camera.preview = AVCaptureVideoPreviewLayer(session: camera.session)
camera.preview.frame = view.frame
//your own properies
camera.preview.videoGravity = .resizeAspectFill
view.layer.addSublayer(camera.preview)
//starting session
camera.session.startRunning()
return view
}
func updateUIView(_ uiView: UIViewType, context: Context) { }
}
class CameraModel: NSObject, ObservableObject, AVCapturePhotoCaptureDelegate {
@Published var session = AVCaptureSession()
@Published var preview: AVCaptureVideoPreviewLayer!
@Published var output = AVCapturePhotoOutput()
func setUp() {
do {
self.session.beginConfiguration()
let device = AVCaptureDevice.default(for: .video)
let input = try AVCaptureDeviceInput(device:device!)
if self.session.canAddInput(input) {
self.session.addInput(input)
}
if self.session.canAddOutput(self.output) {
self.session.addOutput(self.output)
}
self.session.commitConfiguration()
}catch {
print(error.localizedDescription)
}
}
}
I solved the issue by following:
adapting preview frame to bounds
instead of frame
.
when I detect change of device orientation on some superview View
, I change the value of deviceRotation
variable, which automatically triggers updateUIView
function.
struct CameraPreview: UIViewRepresentable {
@ObservedObject var camera: CameraModel
var deviceRotation: UIDeviceOrientation
func makeUIView(context: Context) -> UIView {
let view = UIView(frame: UIScreen.main.bounds)
camera.preview = AVCaptureVideoPreviewLayer(session: camera.session)
camera.preview.frame = view.bounds
//your own properies
camera.preview.videoGravity = .resizeAspectFill
view.layer.addSublayer(camera.preview)
//starting session
camera.session.startRunning()
return view
}
func updateUIView(_ uiView: UIViewType, context: Context) {
let view = UIView(frame: UIScreen.main.bounds)
camera.preview.frame = view.bounds
}
}
manually setting AVCaptureVideoOrientation
on AVCaptureConnection
to correct device orientation. I do this every time when i detect orientation change:
previewConnection.videoPreviewLayer?.connection?.videoOrientation = orientation.videoOrientation