简体   繁体   中英

Issues presenting UIViewController in Xcode 11, SwiftUI

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.

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:

在此处输入图像描述

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM