I would like to have a camera view show up in sheet view to be able to scan barcodes.For some reason, the camera layer created doesn't want to appear in the sheet view, even though the green dot appears on the iPhone, or the logs say that everything is fine.
// MainView.swift@State private var showScanSheet = falsevar body: some View { NavigationStack { VStack { ... }.sheet(isPresented: $showScanSheet) { ScannerView() } }}
// ScannerView.swiftimport SwiftUIimport AVKitstruct ScannerView: View { @State private var isScanning: Bool = false @State private var session: AVCaptureSession = .init() @State private var cameraPermission: Permission = .idle @State private var barcodeOutput: AVCaptureMetadataOutput = .init() @State private var errorMessage: String = "" @State private var showError: Bool = false @Environment(\.openURL) private var openURL @StateObject private var barcodeDelegate = BarcodeScannerDelegate() var body: some View { GeometryReader { let size = $0.size ZStack { CameraView(frameSize: CGSize(width: size.width, height: 200), session: $session).scaleEffect(0.97) RoundedRectangle(cornerRadius: 10, style: .circular) .trim(from: 0.55, to: 0.60) .stroke(Color.red, style: StrokeStyle(lineWidth: 5, lineCap: .round, lineJoin: .round)) .padding() RoundedRectangle(cornerRadius: 10, style: .circular) .trim(from: 0.55, to: 0.60) .stroke(Color.red, style: StrokeStyle(lineWidth: 5, lineCap: .round, lineJoin: .round)) .rotationEffect(.init(degrees: 180)) .padding() RoundedRectangle(cornerRadius: 10, style: .circular) .trim(from: 0.40, to: 0.45) .stroke(Color.red, style: StrokeStyle(lineWidth: 5, lineCap: .round, lineJoin: .round)) .padding() RoundedRectangle(cornerRadius: 10, style: .circular) .trim(from: 0.40, to: 0.45) .stroke(Color.red, style: StrokeStyle(lineWidth: 5, lineCap: .round, lineJoin: .round)) .rotationEffect(.init(degrees: 180)) .padding() } .frame(width: size.width, height: 200) .frame(maxWidth: .infinity, maxHeight: .infinity) } .onAppear(perform: checkCameraPermission) .alert(errorMessage, isPresented: $showError) { if cameraPermission == .denied { Button("Settings") { let settingsString = UIApplication.openSettingsURLString if let settingsURL = URL(string: settingsString) { openURL(settingsURL) } } Button("Cancel", role: .cancel) {} } } } func checkCameraPermission() { print("Checking camera permission") Task { switch AVCaptureDevice.authorizationStatus(for: .video) { case .authorized: cameraPermission = .approved setupCamera() case .notDetermined, .denied, .restricted: if await AVCaptureDevice.requestAccess(for: .video) { cameraPermission = .approved setupCamera() } else { cameraPermission = .denied presentError("Please provide access to the camera for scanning barcodes.") } default: break } print(cameraPermission) } } func setupCamera() { do { guard let device = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera, .builtInUltraWideCamera], mediaType: .video, position: .back).devices.first else { presentError("Unknown error.") return } let input = try AVCaptureDeviceInput(device: device) guard session.canAddInput(input), session.canAddOutput(barcodeOutput) else { presentError("Unknown error.") return } session.beginConfiguration() session.addInput(input) session.addOutput(barcodeOutput) barcodeOutput.metadataObjectTypes = [.upce, .ean8, .ean13] barcodeOutput.setMetadataObjectsDelegate(barcodeDelegate, queue: .main) session.commitConfiguration() DispatchQueue.global(qos: .background).async { session.startRunning() } } catch { presentError(error.localizedDescription) } } func presentError(_ message: String) { errorMessage = message showError.toggle() }}
// BarcodeScannerDelegate.swiftimport Foundationimport AVKitclass BarcodeScannerDelegate: NSObject, ObservableObject, AVCaptureMetadataOutputObjectsDelegate { func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { if let metadataObject = metadataObjects.first { guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return } guard let scannedCode = readableObject.stringValue else { return } print(scannedCode) } }}
// CameraView.swiftimport SwiftUIimport AVKitstruct CameraView: UIViewRepresentable { var frameSize: CGSize @Binding var session: AVCaptureSession func makeUIView(context: Context) -> UIView { let view = UIViewType(frame: CGRect(origin: .zero, size: frameSize)) view.backgroundColor = .clear let cameraLayer = AVCaptureVideoPreviewLayer(session: session) cameraLayer.frame = .init(origin: .zero, size: frameSize) cameraLayer.videoGravity = .resizeAspectFill cameraLayer.masksToBounds = true view.layer.addSublayer(cameraLayer) return view } func updateUIView(_ uiView: UIViewType, context: Context) { }}
However, the app works completely as expected when instead of using a sheet
, I use a fullScreenCover
or a navigationDestination
in MainView.swift.
when using with fullScreenCover
Thank you for your help.