在 SwiftUI 类中调用包装的 UIViewController 函数时获得 nil

Posted

技术标签:

【中文标题】在 SwiftUI 类中调用包装的 UIViewController 函数时获得 nil【英文标题】:Getting nil when calling a wrapped UIViewController's function in a SwiftUI class 【发布时间】:2019-11-13 19:46:51 【问题描述】:

所以下面是我拥有的一个简单的拍照ViewController

final class TakePhotoViewController : UIViewController, AVCapturePhotoCaptureDelegate 
    var captureSession : AVCaptureSession!
    var cameraOutput : AVCapturePhotoOutput!
    var previewLayer : AVCaptureVideoPreviewLayer!
    let device = AVCaptureDevice.default(for: .video)!

    override func viewDidLoad()
        print("viewDidLoad")
        setupCameraLayouts()
    

   private func setupCameraLayouts()
        print("setupCameraLayouts")
        captureSession = AVCaptureSession()
        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)

        captureSession.sessionPreset = AVCaptureSession.Preset.hd1920x1080
        cameraOutput = AVCapturePhotoOutput()
        previewLayer.frame = CGRect(x: view.frame.origin.x, y: view.frame.origin.y+view.frame.height/13, width: view.frame.width, height: view.frame.height/1.2475)

        do 
            try device.lockForConfiguration()
         catch 
            return
        

        device.focusMode = .continuousAutoFocus
        device.unlockForConfiguration()
    

    private func startCamera()
        print("startCamera")
        if let input = try? AVCaptureDeviceInput(device: device) 
            if captureSession.canAddInput(input) 
                captureSession.addInput(input)
                if captureSession.canAddOutput(cameraOutput)
                    captureSession.addOutput(cameraOutput)
                    view.layer.addSublayer(previewLayer)
                    captureSession.startRunning()
                 else 
                    print("else in : captureSession.canAddOutput(cameraOutput)")
                
             else 
                print("else in : captureSession.canAddInput(input)")
            
         else 
            print("else in : input = try? AVCaptureDeviceInput(device: device)")
        
    

    func cameraPressed()
        print("cameraPressed")
        let settings = AVCapturePhotoSettings()
        let previewPixelType = settings.availablePreviewPhotoPixelFormatTypes.first!
        let previewFormat = [
            kCVPixelBufferPixelFormatTypeKey as String: previewPixelType,
            kCVPixelBufferWidthKey as String: 160,
            kCVPixelBufferHeightKey as String: 160
        ]
        settings.previewPhotoFormat = previewFormat
        cameraOutput.capturePhoto(with: settings, delegate: self)
    

    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?)
        print("photoOutput")
        captureSession.stopRunning()
        print("Got something")
    


extension TakePhotoViewController : UIViewControllerRepresentable 

    public typealias UIViewControllerType = TakePhotoViewController

    func makeUIViewController(context: UIViewControllerRepresentableContext<TakePhotoViewController>) -> TakePhotoViewController 
        print("makeUIViewController")
        return TakePhotoViewController()
    

    func updateUIViewController(_ uiViewController: TakePhotoViewController, context: UIViewControllerRepresentableContext<TakePhotoViewController>) 
        print("updateUIViewController")
    

如您所见,我使用 UIViewControllerRepresentable 将其“包装”,因此我可以在 SwiftUI 视图中使用它。除非有更好的方法来做到这一点,否则我发现这是唯一的方法。

下面是我调用它的 SwiftUI 类。

struct ContentView: View 
    let TPVC = TakePhotoViewController()
    var body: some View 
        VStack 
            TPVC.startCamera()
            Button(action: 
                self.TPVC.cameraPressed()
            ) 
                Text("Hello World")
                    .fontWeight(.bold)
                    .font(.title)
                    .padding()
                    .background(Color.purple)
                    .cornerRadius(40)
                    .foregroundColor(.white)
                    .padding(10)
                    .overlay(
                        RoundedRectangle(cornerRadius: 40)
                            .stroke(Color.purple, lineWidth: 5)
                    )
            
        
    

所以我知道(从打印语句)正在调用 TakePhotoVC 并且 nil 错误变量(实际上是所有变量)不是 nil。 话虽如此,当错误发生时(即我单击按钮时),各种变量(captureSession、CaptureOutput)都为零,这会导致明显的错误。在 ContentView 中,我为类实例创建了一个变量,因此我可以随时引用它,但似乎如果再次调用它,它会创建一个全新的类引用/实例

【问题讨论】:

【参考方案1】:

您的 ContentView 是一个结构,即。值,它会在每次 UI 刷新时重新创建,因此您的 TPVC 也会重新创建。

此外,UIViewControllerRepresentable 预计是要在 ViewBuilder 中使用的某个 View。所以在你的代码中这是错误的概念。

教程Interfacing with UIKit应该对你有所帮助。

【讨论】:

以上是关于在 SwiftUI 类中调用包装的 UIViewController 函数时获得 nil的主要内容,如果未能解决你的问题,请参考以下文章

Swift之深入解析SwiftUI属性包装器如何处理结构体

可以直接使用 Publisher 作为 SwiftUI 中的 @ObjectBinding 属性吗?

派生自抽象基类并调用另一个类中的方法的 C++ 包装类,该类也派生自抽象基类

SwiftUI 包装 UICollectionView 并使用组合布局

SwiftUI 中的动画绑定

在 SwiftUI 中使用 @State 属性包装器未更新视图