I'm trying to implement a camera preview view in SwiftUI, for which I have the following code:
import SwiftUI
import AVFoundation
struct CameraPreview: UIViewRepresentable {
let session: AVCaptureSession
func makeUIView(context: Context) -> UIView {
let view = UIView()
view.backgroundColor = .gray
let videoPreviewLayer = AVCaptureVideoPreviewLayer(session: session)
videoPreviewLayer.frame = view.bounds
videoPreviewLayer.videoGravity = .resizeAspectFill
videoPreviewLayer.connection?.videoOrientation = .portrait
view.layer.addSublayer(videoPreviewLayer)
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
for layer in uiView.layer.sublayers ?? [] {
layer.frame = uiView.bounds
}
}
}
However I do see the gray background view that I set on the view, but it never starts showing the camera output. I've set a AVCaptureVideoDataOutputSampleBufferDelegate
class and I can see the frames being captured and processed, yet for some reason it does not start rendering the output.
I have this other snippet that DOES render the output, but it does so by setting the preview layer as the root layer which is what I want to avoid, here's the code that works:
struct CameraPreview: UIViewRepresentable {
let session: AVCaptureSession
func makeUIView(context: Context) -> UIView {
let view = VideoView()
view.backgroundColor = .gray
view.previewLayer.session = session
view.previewLayer.videoGravity = .resizeAspectFill
view.previewLayer.connection?.videoOrientation = .portrait
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
for layer in uiView.layer.sublayers ?? [] {
layer.frame = uiView.bounds
}
}
class VideoView: UIView {
override class var layerClass: AnyClass {
AVCaptureVideoPreviewLayer.self
}
var previewLayer: AVCaptureVideoPreviewLayer {
layer as! AVCaptureVideoPreviewLayer
}
}
}
Some examples I found showed I should be able to show the preview like I do in the first example. I've tried initializing the session with inputs before and after the preview view is created and I've gotten the same result. Am I missing anything? am I not retaining the layer or is there a special configuration for the session to look out for? to make it work I simply swap the implementations and the one with the inner class does render?
Any help is really appreciated.
Some resources: