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

Posted

技术标签:

【中文标题】在 Xcode 11、SwiftUI 中呈现 UIViewController 的问题【英文标题】:Issues presenting UIViewController in Xcode 11, SwiftUI 【发布时间】:2020-07-15 22:42:51 【问题描述】:

我有一个嵌入在 UIViewController 中的 QR 码扫描仪。我一直在查看其他帖子和博客,但它们都是单页应用程序,它们在应用程序启动时将 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)
        
    

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

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

我当然想解决这个问题,但我也想知道是什么问题,以便下次知道。

【问题讨论】:

你有没有试过找出viewDidLoad的用途? 既然您指出这一点并没有那么谨慎,我会确保对其进行调查。尽管这是一个反问,但我也用我目前的知识来回答,这样我就可以在未来进行比较;我的印象是 viewDidLoad 是在整体视图加载后运行的东西(就像在 contentview 加载时一样),所以在这种情况下 viewDidLoad 是没用的,如果不是在实际按下按钮之前拉起扫描仪的话。 @ElTomato 自从我上次发表评论以解决我的问题以来,我很难找到足够的时间,你能给我一个解决的代码示例,以便我看看哪里出错了吗?我理解为什么 viewDidLoad 在这种情况下不合适,但我不知道如何调整我的代码以删除那些引用行。也刚刚意识到我在另一个帐户上发布了此评论,但我们是同一个人。 【参考方案1】:

我们不能在SwiftUI 中直接调用UIViewController。 需要UIViewControllerRepresentable 提供UIViewController

你可以在这里阅读:

https://developer.apple.com/documentation/swiftui/uiviewcontrollerrepresentable https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit

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

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


还有你structScanView:

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

相机视图,我已经把相机视图拖到了下面,这样你就可以在后台看到按钮屏幕了:

附:不要忘记在info.plist中添加Privacy - Camera Usage Description

【讨论】:

这个解决方案适用于呈现视图,我现在面临的问题是它会拉起视图但相机视图是空白的?图片参考这里:imgur.com/a/gFw596A 我不久前添加了该权限,但相机视图中仍然没有任何内容。问题可能是我试图从中提取视图的视图是 contentview 下的嵌套视图吗?我尝试制作一个可观察对象和环境对象,以便能够从内容视图中拉出扫描仪,但该代码根本不起作用。我真的很困惑问题是什么,我将制作一个空白项目并使用此代码查看问题是否基于视图嵌套的事实。 我刚刚使用我提供的代码和您提供的代码创建了一个项目,它按预期工作。这给我留下了一个谜,为什么它不适用于我的原始项目?我在原始项目的 info.plist 中添加了摄像头访问权限,但它没有像在空白项目上那样请求访问摄像头? 更多背景信息,因为我尝试了更多(不成功)解决方案来解决我的问题;我使用一个名为 viewRouter 的可观察对象来导航到我的视图的单独子树,因为对于不同的用户类型有 5-6 个不同的离散导航树,不同树的初始细分占据了我的内容视图。我尝试专门为 qr 扫描仪添加另一个分支,认为它可能与嵌套在 contentview 之外有关,但即使它直接在 contentview 中,同样的问题仍然存在。有什么我可以提供更多帮助的吗?【参考方案2】:

我不是 xCode 专家。更不用说 SwiftUI 专家了。但看起来您正试图从 SwiftUI 视图导航到 UIKit 视图。这让我愣了一分钟。

无论如何,您都希望将按钮包装在 NavigationView 中。 https://www.hackingwithswift.com/articles/216/complete-guide-to-navigationview-in-swiftui

【讨论】:

以上是关于在 Xcode 11、SwiftUI 中呈现 UIViewController 的问题的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 工作表在第一次呈现时被关闭

Swift UI 实时预览画布不适用于 macOS Catalina 和 Xcode 11.0

从 NavigationView 呈现的 SwiftUI 关闭模式表(Xcode Beta 5)

未找到 SwiftUI 符号:_$s7SwiftUI9TextFieldVAA4ViewAAMc Xcode 11 beta 3

删除 List Swift UI Xcode 11 中的空行

swiftui 请求 渲染数据