QR码扫描无法正常工作

时间:2017-10-05 08:13:48

标签: ios swift avfoundation qr-code swift4

所以我刚刚接管了一个iOS项目作为他们的第一个内部开发人员,之前这个应用程序是由一个代理商开发的。

该应用程序的一个功能是它需要扫描QR代码 - 通过以前开发人员在AppCoda上执行this教程以实现QR扫描的代码外观。一切看起来都很好,我看不出代码有什么问题,但它没有用。

我还下载了完成的教程项目,当我尝试QR扫描时,该项目有效。我也尝试过复制和粘贴每一行,这样它与工作教程相同但没有运气。

我正在撕扯我的头发试图弄清楚它为什么不起作用。

非常感谢任何帮助!

enum ScanState : Int {
  case newDevice = 1
  case resetDevice = 2
  case replaceDevice = 3
}

class QRScannerViewController: BaseViewController,AVCaptureMetadataOutputObjectsDelegate {
  @IBOutlet var scanZoneView: UIView!
  @IBOutlet var scannerVIew: UIView!
  @IBOutlet var scanInfoLabel: UILabel!

  var capturedQR: String? = nil
  var captureSession:AVCaptureSession?
  var videoPreviewLayer:AVCaptureVideoPreviewLayer?
  var qrCodeFrameView:UIView?
  let supportedBarCodes = [AVMetadataObject.ObjectType.qr, AVMetadataObject.ObjectType.code128, AVMetadataObject.ObjectType.code39, AVMetadataObject.ObjectType.code93, AVMetadataObject.ObjectType.upce, AVMetadataObject.ObjectType.pdf417, AVMetadataObject.ObjectType.ean13, AVMetadataObject.ObjectType.aztec]
  var type = "leg scan"


  var device:Device?
  var state:ScanState = .newDevice

  override func viewDidLoad() {
    super.viewDidLoad()

    scanInfoLabel.text = "Scan your existing\nleg QR code"

    self.navigationController?.navigationBar.dark()

    //self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil)
  }

  override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.navigationController?.setNavigationBarHidden(false, animated: true)
  }

  override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    #if NOHARDWARE
      moveToNextViewController()
    #else
      initiateCapture()
    #endif
  }

  func initiateCapture() {
    let captureDevice = AVCaptureDevice.default(for: AVMediaType.video)
    // Get an instance of the AVCaptureDeviceInput class using the previous device object.
    var error:NSError?
    let input: AnyObject!
    do {
      input = try AVCaptureDeviceInput(device: captureDevice!) as AVCaptureDeviceInput
    } catch let error1 as NSError {
      error = error1
      input = nil
    } catch _ {
      input = nil
    }

    if (error != nil) {
      // If any error occurs, simply log the description of it and don't continue any more.
      print("\(error?.localizedDescription)")
      return
    }
    // Initialize the captureSession object.
    captureSession = AVCaptureSession()
    // Set the input device on the capture session.
    captureSession?.addInput(input as! AVCaptureInput)

    // Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
    let captureMetadataOutput = AVCaptureMetadataOutput()
    captureSession?.addOutput(captureMetadataOutput)

    // Set delegate and use the default dispatch queue to execute the call back
    captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
    captureMetadataOutput.metadataObjectTypes = supportedBarCodes

    // Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer.
    videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
    videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
    videoPreviewLayer?.frame = scannerVIew.layer.bounds
    scannerVIew.layer.addSublayer(videoPreviewLayer!)

    // Start video capture.
    captureSession?.startRunning()


    // Initialize QR Code Frame to highlight the QR code
    qrCodeFrameView = UIView()
    qrCodeFrameView?.layer.borderColor = UIColor.green.cgColor
    qrCodeFrameView?.layer.borderWidth = 2
    scannerVIew.addSubview(qrCodeFrameView!)
    scannerVIew.bringSubview(toFront: qrCodeFrameView!)
    //qrCapturedLabel.text = "No QR code is detected"

  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
  }

  func metadataOutput(captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {

    // Check if the metadataObjects array is not nil and it contains at least one object.
    if metadataObjects == nil || metadataObjects.count == 0 {
      qrCodeFrameView?.frame = CGRect.zero

      return
    }

    // Get the metadata object.
    let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject

    // Here we use filter method to check if the type of metadataObj is supported
    // Instead of hardcoding the AVMetadataObjectTypeQRCode, we check if the type
    // can be found in the array of supported bar codes.
    if supportedBarCodes.filter({ $0 == metadataObj.type }).count > 0 {
      // If the found metadata is equal to the QR code metadata then update the status label's text and set the bounds
      let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj as AVMetadataMachineReadableCodeObject) as! AVMetadataMachineReadableCodeObject

      let intersectionRect = barCodeObject.bounds.intersection(self.scanZoneView.frame)

      if !intersectionRect.isNull &&
        (intersectionRect.size.width * intersectionRect.size.height) > self.scanZoneView.bounds.width * self.scanZoneView.bounds.height / 7 {

        qrCodeFrameView?.frame = barCodeObject.bounds

        if process(metadataObj.stringValue!) {
          captureSession?.stopRunning()
        }
      }
    }
  }

  @IBAction func didTapCancel(_ sender: AnyObject) {
    self.dismiss(animated: true, completion: nil)
  }
}

extension QRScannerViewController {


  func process(_ scanText : String) -> Bool {

    var legCode : String

    let codeComponents = scanText.components(separatedBy: ";")
    if codeComponents.count > 0 {
      legCode = codeComponents[0]
    } else {
      // Invalid number of parameters seperated by a ;
      return false
    }

    // TODO Validate the LEG to LEG-XXXXX
    if legCode.hasPrefix("LEG-") {
      let delta: Int64 = 1 * Int64(NSEC_PER_SEC)
      let time = DispatchTime.now() + Double(delta) / Double(NSEC_PER_SEC)

      DispatchQueue.main.asyncAfter(deadline: time, execute: {

        switch self.state {

        case .resetDevice:

          let realm = try! Realm()
          let deviceList = realm.objects(Device.self)
          let lc = legCode

          self.device = deviceList.filter("legCode = %@", lc).first

          if self.device == nil {

            // TODO Error message: Device not associated with LEG
            let vc = ErrorViewController.createErrorViewController(.DeviceNotFound)
            self.present(vc, animated: true, completion: nil)
            return
          }

          self.moveToNextViewController()

        default:
          self.presentingViewController?.dismiss(animated: true, completion: nil)
        }

      });

      return true
    }

    return false
  }

  func moveToNextViewController() {
    let inspectionStoryboard = UIStoryboard(name: "Impact", bundle: nil)

    if let resetVC = inspectionStoryboard.instantiateViewController(withIdentifier: ImpactDetectionViewController.storyboardID) as? ImpactDetectionViewController {
      resetVC.device = device
      // TODO Pass the impact type across too when the G2 API is set
      self.navigationController?.pushViewController(resetVC, animated: false)
    }
  }

  @IBAction func cancelToVC(_ segue: UIStoryboardSegue) { }

}

修改

通过不工作,我的意思是AVCaptureMetadataOutputObjectsDelegate的委托从未被调用,因此它似乎永远不会检测到QR代码。在AppCoda教程中,它覆盖了一个绿色方块,用于检测QR码,但是当我将该代码放入此应用程序时,这种情况就不会发生。

相机实际上正在运行但从未检测到QR码。

2 个答案:

答案 0 :(得分:9)

原来这个答案看起来很简单(而且很烦人)!当然在Swift 4中,代表们已经稍微重命名了。要解决所有必须改变的问题

  func metadataOutput(captureOutput: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {

  func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {

答案 1 :(得分:-1)

您未将班级与AVCaptureMetadataOutput代表联系起来,这就是为什么AVCaptureMetadataOutputObjectsDelegate函数未被调用的原因。更多信息:https://developer.apple.com/documentation/avfoundation/avcapturemetadataoutputobjectsdelegate

试试这个:

// Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
    let captureMetadataOutput = AVCaptureMetadataOutput()
    captureMetadataOutput.metadataObjectsDelegate = self // assign self to the delegate.
    captureSession?.addOutput(captureMetadataOutput)