ios - 在 Xcode 11、SwiftUI 中呈现 UIViewController 的问题

标签 ios swift swiftui qr-code xcode11

我有一个二维码扫描仪,它嵌入在 UIViewController 中。我一直在查看其他帖子和博客,但都是单页应用程序,它们在应用程序启动时将 UIViewController 作为主要应用程序呈现。我只需要它在按钮操作中打开,但出于某种原因我无法让它正确显示扫描仪?

这是二维码:

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
    }
}

这是我(非常简单的)尝试在按下按钮时打开它:

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)
        }
    }
}

我已经尝试查看有关这些类型问题的其他一些 StackOverflow 帖子,但有些帖子要么使用已弃用的扫描二维码的方式,要么与我面临的问题无关。

我确信这很简单,但我的 Swift/Xcode 专业知识主要涉及设计和样式以及 API 调用的基本知识,但我不一定了解不同的 View 层次结构和调用方法是如何工作的。

当然,我想解决这个问题,但我也想知道问题出在哪里,以便下次知道。

最佳答案

我们不能在SwiftUI中直接调用UIViewController。 需要 UIViewControllerRepresentable 来呈现一个 UIViewController

你可以在这里阅读:

试试这个,它工作正常,我可以通过摄像头看到内容:

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()
    }

}

在你的 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()
        }
    }
}

相机 View ,我将相机 View 拖到下面,这样您就可以在背景中看到按钮屏幕:

enter image description here

附言不要忘记在 info.plist

中添加 Privacy - Camera Usage Description

关于ios - 在 Xcode 11、SwiftUI 中呈现 UIViewController 的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62924826/

相关文章:

ios - NSURLConnection didReceiveData 未加载数据

ios - 创建 NSManagedObject 属性值的副本

ios - 在数组中追加一个对象

ios - Swiftui Pan Gesture 获取 View 的水平位置

swift - 将多个闭包作为参数快速传递给函数

ios - 从 UIViewController 中的 SwiftUIView 分配 @state var

ios - 关闭应用程序并在 appstart 上启动 webview

ios - cellForRowAtIndexPath 中的 dequeueReusableCellWithIdentifier 中的单元格直到滚动才显示

ios - 如何更改 "Sign In with Apple"按钮的宽度?

SwiftUI - 如何制作启动/停止计时器