当我尝试获取边界时,为啥 previewLayer 返回 nil?

Posted

技术标签:

【中文标题】当我尝试获取边界时,为啥 previewLayer 返回 nil?【英文标题】:Why is previewLayer returning nil when I try to get bounds?当我尝试获取边界时,为什么 previewLayer 返回 nil? 【发布时间】:2017-04-17 01:39:46 【问题描述】:

正在从另一个故事板上的按钮调用此视图控制器。每次我点击按钮我都会得到

致命错误:在展开可选值时意外发现 nil 2017-04-16 21:00:39.929042 FaceOnACase[757:377279] 致命错误:在展开可选值时意外发现 nil

就行self.previewLayer?.frame = self.previewView.bounds.

我之前在我正在开发的一个单独的单视图应用程序中使用了此代码,但是当我将代码传输过来并连接 IBOutlets 时,由于某种原因我无法让它工作。

我找不到类似的问题,我不确定如何更正。任何帮助表示赞赏,谢谢。

import UIKit
import AVFoundation

class CaptureController: UIViewController, AVCapturePhotoCaptureDelegate 
    var captureSesssion: AVCaptureSession!
    var cameraOutput: AVCapturePhotoOutput!
    var previewLayer: AVCaptureVideoPreviewLayer!
    @IBOutlet weak var capturedImage: UIImageView!
    @IBOutlet weak var previewView: UIView!
    override func viewDidLoad() 
        super.viewDidLoad()
        captureSesssion = AVCaptureSession()
        captureSesssion.sessionPreset = AVCaptureSessionPresetPhoto
        cameraOutput = AVCapturePhotoOutput()
        let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
        do 
            let input = try AVCaptureDeviceInput(device: device)
            if (captureSesssion.canAddInput(input)) 
                captureSesssion.addInput(input)
                if (captureSesssion.canAddOutput(cameraOutput)) 
                    captureSesssion.addOutput(cameraOutput)
                    previewLayer = AVCaptureVideoPreviewLayer(session: captureSesssion)
                    self.previewLayer?.frame = self.previewView.bounds
                    previewView.layer.addSublayer(previewLayer)
                    captureSesssion.startRunning()
                
             else 
                print("issue here : captureSesssion.canAddInput")
            
         catch let error 
            print("error \(error.localizedDescription)")
        
    
    // Take picture button
    @IBAction func didPressTakePhoto(_ sender: UIButton) 
        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)
    
    // callBack from take picture
    func capture(_ captureOutput: AVCapturePhotoOutput,  didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?,  previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings:  AVCaptureResolvedPhotoSettings, bracketSettings:   AVCaptureBracketedStillImageSettings?, error: Error?) 
        if let error = error 
            print("error occured : \(error.localizedDescription)")
        
        if  let sampleBuffer = photoSampleBuffer,
            let previewBuffer = previewPhotoSampleBuffer,
            let dataImage =  AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer:  sampleBuffer, previewPhotoSampleBuffer: previewBuffer) 
            print(UIImage(data: dataImage)?.size as Any)
            let dataProvider = CGDataProvider(data: dataImage as CFData)
            let cgImageRef: CGImage! = CGImage(jpegDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: true, intent: .defaultIntent)
            let image = UIImage(cgImage: cgImageRef, scale: 1.0, orientation: UIImageOrientation.right)
            self.capturedImage.image = image
         else 
            print("some error here")
        
    
    // This method you can use somewhere you need to know camera permission   state
    func askPermission() 
        print("here")
        let cameraPermissionStatus =  AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo)
        switch cameraPermissionStatus 
        case .authorized:
            print("Already Authorized")
        case .denied:
            print("denied")
            let alert = UIAlertController(title: "Sorry :(" , message: "But  could you please grant permission for camera within device settings",  preferredStyle: .alert)
            let action = UIAlertAction(title: "Ok", style: .cancel,  handler: nil)
            alert.addAction(action)
            present(alert, animated: true, completion: nil)
        case .restricted:
            print("restricted")
        default:
            AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: 
                [weak self]
                (granted :Bool) -> Void in
                if granted == true 
                    // User granted
                    print("User granted")
                    DispatchQueue.main.async()
                        //Do smth that you need in main thread
                    
                
                else 
                    // User Rejected
                    print("User Rejected")
                    DispatchQueue.main.async()
                        let alert = UIAlertController(title: "WHY?" , message:  "Camera it is the main feature of our application", preferredStyle: .alert)
                        let action = UIAlertAction(title: "Ok", style: .cancel, handler: nil)
                        alert.addAction(action)
                        self?.present(alert, animated: true, completion: nil)
                    
                
            )
        
    

【问题讨论】:

【参考方案1】:

previewLayer返回nil。是previewView,即nil。确保您已将其正确连接为情节提要中的插座。

你现在做的也太早了。您无法在 viewDidLoad 中启动捕获会话。请记住,这意味着视图存在,但它甚至还没有出现在界面中!您至少需要一个界面才能启动捕获会话。

【讨论】:

好的,我检查了,我把它正确连接了。现在,我应该把这些放在 viewWillAppear 中而不是 viewDidLoad 中吗? 因为现在一切似乎都在正常工作。谢谢顺便说一句! 我建议使用viewDidLayoutSubviews,因为您依赖于self.previewView.bounds,并且在此之前它不会被正确设置。您将需要一个 Bool 标志,以便您的代码只运行一次(因为 viewDidLayoutSubviews 被多次调用)。 因此,由于 viewDidLayoutSubviews 不需要 Bool arg,因此我将相机块(从 captureSession = AVCaptureSession() 开始到 do-try-catch 块的末尾)包装在 if 语句中,将像 loadCamera 这样的 var 设置为最初为 false,但如果完成则为 true? 因为当我连接插座并将它放在 viewDidLoad() 中时,它工作得很好(谢谢!),但是在我把它放入 viewDidLayoutSubviews() 之后,视图加载但相机不工作。再次感谢您的帮助!

以上是关于当我尝试获取边界时,为啥 previewLayer 返回 nil?的主要内容,如果未能解决你的问题,请参考以下文章

当我尝试绘制决策边界时形状错误

为啥当我尝试获取我的 MAC 地址时得到一个空字符串?

无法获取 Geohash 的边界框

我尝试绘制决策边界时的形状错误

类型“RelayObservable<unknown>”上不存在属性“then”。当我尝试在反应中使用中继获取数据时。我不知道为啥会出现这个错误

从 topojson 获取各个国家的边界​​框