Delegate found nil while trying to capture the photo.

huangapple go评论63阅读模式
英文:

Delegate found nil while trying to capture the photo

问题

我成功地创建了自定义相机,包括实时预览等等。但是,当我尝试拍照时,出现了以下错误:

Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

这是我的代码:

class CameraService {
    var session: AVCaptureSession?
    var delegate: AVCapturePhotoCaptureDelegate?
    
    let output = AVCapturePhotoOutput()
    let previewLayer = AVCaptureVideoPreviewLayer()
    
    func start(delegate: AVCapturePhotoCaptureDelegate, completion: @escaping (Error?) -> ()) {
        self.delegate = delegate
        self.checkPermissions(completion: completion)
    }
    
    private func checkPermissions(completion: @escaping (Error?) -> ()) {
        switch AVCaptureDevice.authorizationStatus(for: .video) {
        case .notDetermined:
            AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
                guard granted else { return }
                DispatchQueue.main.async {
                    self?.setupCamera(completion: completion)
                }
            }
        case .restricted:
            break
        case .denied:
            break
        case .authorized:
            setupCamera(completion: completion)
        @unknown default:
            break
        }
    }
    
    private func setupCamera(completion: @escaping (Error?) -> ()) {
        let session = AVCaptureSession()
        if let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front) {
            do {
                let input = try AVCaptureDeviceInput(device: device)
                if session.canAddInput(input) {
                    session.addInput(input)
                }
                
                if session canAddOutput(output) {
                    session.addOutput(output)
                }
                previewLayer.videoGravity = .resizeAspectFill
                
                previewLayer.session = session
                
                DispatchQueue.global(qos: .userInitiated).async {
                    session.startRunning()
                }
                
                self.session = session
            } catch {
                completion(error)
            }
        }
    }
    
    func capturePhoto(with settings: AVCapturePhotoSettings = AVCapturePhotoSettings()) {
        output.capturePhoto(with: settings, delegate: delegate!)  // 这是发生错误的地方。
    }
}

struct CameraView: UIViewControllerRepresentable {
    // ...
}

struct CustomCameraView: View {
    // ...
}

我知道我需要安全解包可选值,但我只是为了测试目的才这样做。这是很多代码,但如果有人仔细查看问题,我将不胜感激。

英文:

I successfully made custom camera, with live preview etc. but, when I try to capture the photo this error comes up:

Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

This is my code

class CameraService {
var session: AVCaptureSession?
var delegate: AVCapturePhotoCaptureDelegate?


let output = AVCapturePhotoOutput()
let previewLayer = AVCaptureVideoPreviewLayer()

func start(delegate: AVCapturePhotoCaptureDelegate, completion:@escaping (Error?) -> ()) {
    self.delegate = delegate
    self.checkPermissions(completion: completion)
}

private func checkPermissions(completion:@escaping (Error?) -> ()){
    switch AVCaptureDevice.authorizationStatus(for: .video){
            
        case .notDetermined:
            AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
                guard granted else { return }
                
                DispatchQueue.main.async {
                    self?.setupCamera(completion: completion)
                }
            }
        case .restricted:
            break
        case .denied:
            break
        case .authorized:
            setupCamera(completion: completion)
        @unknown default:
            break
    }
}

private func setupCamera(completion:@escaping (Error?) -> ()) {
    let session = AVCaptureSession()
    if let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front){
        do {
            let input = try AVCaptureDeviceInput(device: device)
            if session.canAddInput(input) {
                session.addInput(input)
            }
            
            if session.canAddOutput(output) {
                session.addOutput(output)
            }
            previewLayer.videoGravity = .resizeAspectFill
            
            previewLayer.session = session
            
            DispatchQueue.global(qos:.userInitiated).async {
                session.startRunning()
            }
          
            self.session = session
            
        } catch {
            completion(error)
        }
    }
}

func capturePhoto(with settings: AVCapturePhotoSettings = AVCapturePhotoSettings()) {
    
    output.capturePhoto(with: settings, delegate: delegate!)  // This is where the 
    error comes up.
}
  }

CameraView

struct CameraView: UIViewControllerRepresentable {
    typealias UIViewControllerType = UIViewController
    
    let cameraService: CameraService
    let didFinishProcessingPhoto: (Result<AVCapturePhoto, Error>) -> ()
    
    func makeUIViewController(context: Context) -> UIViewController {
        
        cameraService.start(delegate: context.coordinator) { err in
            if let err = err {
                didFinishProcessingPhoto(.failure(err))
                return
            }
        }
        
        let viewController = UIViewController()
        viewController.view.backgroundColor = .black
        viewController.view.layer.addSublayer(cameraService.previewLayer)
        cameraService.previewLayer.frame = viewController.view.bounds
        return viewController
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self, didFinishProcessingPhoto: didFinishProcessingPhoto)
    }
    
    func updateUIViewController(_ uiViewController: UIViewController, context: Context) { }
    
    class Coordinator: NSObject, AVCapturePhotoCaptureDelegate {
        let parent: CameraView
        private var didFinishProcessingPhoto: (Result<AVCapturePhoto, Error>) -> ()
        
        init(_ parent: CameraView,
             didFinishProcessingPhoto: @escaping (Result<AVCapturePhoto, Error>) -> ()) {
            self.parent = parent
            self.didFinishProcessingPhoto = didFinishProcessingPhoto
        }
        
        func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
            if let error = error {
                didFinishProcessingPhoto(.failure(error))
                return
            }
            didFinishProcessingPhoto(.success(photo))
        }
    }
}

CustomCameraView

import SwiftUI
import AVFoundation


struct CustomCameraView: View {
    let cameraService = CameraService()
    @Binding var capturedImage: UIImage?
    
    @Environment(\.presentationMode) private var presentationMode
    var body: some View {
        ZStack {
            CameraView(cameraService: cameraService, didFinishProcessingPhoto: { result in
                switch result {
                    case .success(let photo):
                        if let data = photo.fileDataRepresentation() {
                            capturedImage = UIImage(data: data)
                        } else {
                            print("Error: no image data found.")
                        }
                    case .failure(let err):
                        print(err.localizedDescription)
                }
            })
                .frame(maxWidth:.infinity, maxHeight: .infinity)
                .ignoresSafeArea()
            VStack{
                Button {
                    cameraService.capturePhoto()
                } label: {
                    Circle()
                        .fill(.white)
                        .frame(width: 60, height:60)
                }
            }
        }
    }
}

I know that I need to safe unwrap the optional value, but I did this only because it's for testing purpose.

I know this is a lot of code, but I would be grateful if someone looks closer to the question.

答案1

得分: 1

这是一个错误:

func makeCoordinator() -> Coordinator {
    Coordinator(self, didFinishProcessingPhoto: didFinishProcessingPhoto)
}

应该只是 Coordinator(),因为 selfdidFinish 将会丢失,因为这在一个将被多次重新初始化的结构体内。

此外,实现 updateUIViewController 是必不可少的,以便使用此结构体中的所有新值更新视图控制器和协调器。

这是一个我制作的示例,可以帮助你弄对。

英文:

This is a mistake:

func makeCoordinator() -> Coordinator {
    Coordinator(self, didFinishProcessingPhoto: didFinishProcessingPhoto)
}

It should just be Coordinator(), because self and that didFinish will be lost since this is inside of a struct that will be re-init many times.

Also, it is essential to implement updateUIViewController to update both the view controller and the coordinator with all the new values in this struct.

Here is an example I made to help you get it right.

huangapple
  • 本文由 发表于 2023年3月1日 11:19:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/75599280.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定