TransWikia.com

Issues presenting UIViewController in Xcode 11, SwiftUI

Stack Overflow Asked on December 27, 2021

I have a QR Code scanner, which is embedded in a UIViewController. I’ve been looking around at other posts and blogs but all are single page apps which present the UIViewController as the primary one on launch of the application. I just need it to open inside of a button action, and for some reason I can’t get it to present the scanner correctly?

Here’s the QR Scanner Code:

class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
    var captureSession: AVCaptureSession!
    var previewLayer: AVCaptureVideoPreviewLayer!

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = UIColor.black
        captureSession = AVCaptureSession()

        guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
        let videoInput: AVCaptureDeviceInput

        do {
            videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
        } catch {
            return
        }

        if (captureSession.canAddInput(videoInput)) {
            captureSession.addInput(videoInput)
        } else {
            failed()
            return
        }

        let metadataOutput = AVCaptureMetadataOutput()

        if (captureSession.canAddOutput(metadataOutput)) {
            captureSession.addOutput(metadataOutput)

            metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
            metadataOutput.metadataObjectTypes = [.qr]
        } else {
            failed()
            return
        }

        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer.frame = view.layer.bounds
        previewLayer.videoGravity = .resizeAspectFill
        view.layer.addSublayer(previewLayer)

        captureSession.startRunning()
    }

    func failed() {
        let ac = UIAlertController(title: "Scanning not supported", message: "Your device does not support scanning a code from an item. Please use a device with a camera.", preferredStyle: .alert)
        ac.addAction(UIAlertAction(title: "OK", style: .default))
        present(ac, animated: true)
        captureSession = nil
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        if (captureSession?.isRunning == false) {
            captureSession.startRunning()
        }
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        if (captureSession?.isRunning == true) {
            captureSession.stopRunning()
        }
    }

    func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
        captureSession.stopRunning()

        if let metadataObject = metadataObjects.first {
            guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
            guard let stringValue = readableObject.stringValue else { return }
            AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
            found(code: stringValue)
        }

        dismiss(animated: true)
    }

    func found(code: String) {
        print(code)
    }

    override var prefersStatusBarHidden: Bool {
        return true
    }

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        return .portrait
    }
}

and here’s my (very simplistic) attempt to have it open upon pressing a button:

struct ScanView: View{
    
    var body: some View{
        VStack {
            Button(action: {
                ScannerViewController()
            }) {
                Text("Scan QR Code").foregroundColor(Color.white).padding()
            }.frame(width: 150)
            .background(Color.blue)
            .cornerRadius(25)
        }
    }
}

I’ve tried looking at a few other StackOverflow posts regarding these types of issues, but some are either using deprecated ways of scanning qr codes or are otherwise irrelevant to the issue I’m facing.

I’m sure it’s something fairly simple but my Swift/Xcode expertise mostly regards design and styling with basic knowledge of API calls, but I don’t necessarily understand how the different view hierarchies and calling methods work.

I would like to fix this issue, of course, but I’d also like to know what the issue was so that I can know for next time.

2 Answers

We can not directly call UIViewController in SwiftUI. Need UIViewControllerRepresentable to present a UIViewController.

You can read about it here:

Try this, it is working and I can see the content through camera :

import SwiftUI
import UIKit
import AVFoundation

struct ScannerView: UIViewControllerRepresentable {
    typealias UIViewControllerType = ScannerViewController

    func updateUIViewController(_ uiViewController: ScannerViewController, context: Context) {
        print("update")
    }

    func makeUIViewController(context: Context) -> ScannerViewController {
        return ScannerViewController()
    }

}

and in you struct ScanView:

struct ContentView: View {
    @State private var showingScanner = false
    var body: some View {
        VStack {
            Button(action: {
                self.showingScanner = true
            }) {
                Text("Scan QR Code").foregroundColor(Color.white).padding()
            }.frame(width: 150)
            .background(Color.blue)
            .cornerRadius(25)
        }.sheet(isPresented: $showingScanner) {
            ScannerView()
        }
    }
}

The camera view, I have dragged the camera view below so that you can see the button screen in the background:

enter image description here

P.S. Don't forget to add Privacy - Camera Usage Description in info.plist

Answered by Amit on December 27, 2021

I'm not an xCode expert. Much less a SwiftUI expert. But it looks like you're trying to navigate from a SwiftUI view to a UIKit view. That threw me off for a minute.

Anyways, you're going to want to wrap your button in a NavigationView. https://www.hackingwithswift.com/articles/216/complete-guide-to-navigationview-in-swiftui

Answered by StonedStudio on December 27, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP