【问题标题】:Scanning Barcode or QR code in Swift 3.0 using AVFoundation使用 AVFoundation 在 Swift 3.0 中扫描条形码或二维码
【发布时间】:2016-09-17 13:46:23
【问题描述】:

我正在关注tutorial 并尝试将代码从 Swift 2.0 转换为 3.0。但是当我启动该应用程序时,该应用程序无法正常工作!我的意思是,什么都不会发生!这是我的代码:

视图控制器:

class ViewController: UIViewController ,BarcodeDelegate {

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        let barcodeViewController: BarcodeViewController = segue.destination as! BarcodeViewController
        barcodeViewController.delegate = self

    }



    func barcodeReaded(barcode: String) {
        codeTextView.text = barcode
        print(barcode)
    }

}

BarcodeVC:

import AVFoundation


protocol BarcodeDelegate {

    func barcodeReaded(barcode: String)
}

class BarcodeViewController: UIViewController,AVCaptureMetadataOutputObjectsDelegate {

    var delegate: BarcodeDelegate?
    var captureSession: AVCaptureSession!
    var code: String?


    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        print("works")

        self.captureSession = AVCaptureSession();
        let videoCaptureDevice: AVCaptureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)

        do {

            let videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)

            if self.captureSession.canAddInput(videoInput) {
                self.captureSession.addInput(videoInput)
            } else {
                print("Could not add video input")
            }

            let metadataOutput = AVCaptureMetadataOutput()
            if self.captureSession.canAddOutput(metadataOutput) {
                self.captureSession.addOutput(metadataOutput)

                metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
                metadataOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypePDF417Code]
            } else {
                print("Could not add metadata output")
            }

            let previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
            previewLayer?.frame = self.view.layer.bounds
            self.view.layer .addSublayer(previewLayer!)
            self.captureSession.startRunning()
        } catch let error as NSError {
            print("Error while creating vide input device: \(error.localizedDescription)")
        }



    }



    //I THINK THIS METHOD NOT CALL !
    private func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {

        // This is the delegate'smethod that is called when a code is readed
        for metadata in metadataObjects {
            let readableObject = metadata as! AVMetadataMachineReadableCodeObject
            let code = readableObject.stringValue

            // If the code is not empty the code is ready and we call out delegate to pass the code.
            if  code!.isEmpty {
                print("is empty")

            }else {

                self.captureSession.stopRunning()
                self.dismiss(animated: true, completion: nil)
                self.delegate?.barcodeReaded(barcode: code!)


            }
        }

    }

这是输出:

2016-09-17 18:10:26.000919 BarcodeScaning[2610:674253] [MC] systemgroup.com.apple.configurationprofiles 路径的系统组容器是 /private/var/containers/Shared /SystemGroup/systemgroup.com.apple.configurationprofiles 2016-09-17 18:10:26.007782 BarcodeScaning[2610:674253] [MC] 从公共有效用户设置中读取。

【问题讨论】:

标签: ios swift swift3 avcapturesession


【解决方案1】:

第一步需要声明对任何用户私有数据类型的访问权限,这是 iOS 10 中的新要求。您可以通过向应用程序的Info.plist 添加使用密钥和目的字符串来实现。

因为如果您使用以下框架之一并且未能声明使用情况,您的应用将在首次访问时崩溃:

通讯录、日历、提醒、照片、蓝牙共享、麦克风、相机、位置、健康、HomeKit、媒体库、运动、CallKit、语音识别、SiriKit、电视提供商。

为避免崩溃,您需要将建议的密钥添加到Info.plist

然后系统在要求用户允许访问时显示目的字符串:

有关它的更多信息,您可以使用这篇文章:

我对您的BarcodeViewController 进行了一些修改,以使其正常工作,如下所示:

BarcodeViewController

import UIKit
import AVFoundation

protocol BarcodeDelegate {
   func barcodeReaded(barcode: String)
}

class BarcodeViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {

   var delegate: BarcodeDelegate?

   var videoCaptureDevice: AVCaptureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
   var device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
   var output = AVCaptureMetadataOutput()
   var previewLayer: AVCaptureVideoPreviewLayer?

   var captureSession = AVCaptureSession()
   var code: String?

   override func viewDidLoad() {
      super.viewDidLoad()

      self.view.backgroundColor = UIColor.clear
      self.setupCamera()
   }

   private func setupCamera() {

      let input = try? AVCaptureDeviceInput(device: videoCaptureDevice)

      if self.captureSession.canAddInput(input) {
          self.captureSession.addInput(input)
      }

      self.previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)

      if let videoPreviewLayer = self.previewLayer {
          videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
          videoPreviewLayer.frame = self.view.bounds
          view.layer.addSublayer(videoPreviewLayer)
      }

      let metadataOutput = AVCaptureMetadataOutput()
      if self.captureSession.canAddOutput(metadataOutput) {
          self.captureSession.addOutput(metadataOutput)

          metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
          metadataOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code]
      } else {
          print("Could not add metadata output")
      }
   }

   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 captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
       // This is the delegate's method that is called when a code is read
       for metadata in metadataObjects {
           let readableObject = metadata as! AVMetadataMachineReadableCodeObject
           let code = readableObject.stringValue


           self.dismiss(animated: true, completion: nil)
           self.delegate?.barcodeReaded(barcode: code!)
           print(code!)
       }
   }
}

其中一个重点是声明全局变量并在viewWillAppear(:)viewWillDisappear(:) 方法中启动和停止captureSession。在您之前的代码中,我认为它根本没有被调用,因为它从未进入处理条形码的方法中。

希望对你有所帮助。

【讨论】:

  • 我会在1H奖励赏金(由于限制)
  • @Mc.Lover 没问题,:)
  • 你知道如何指定要扫描的区域(例如只有一个有限的矩形?
  • 嗨@VictorSigler。请问您在哪里找到了实现条形码扫描仪的文档?我只是想看懂你的每一行代码,非常感谢。
  • 完美,但没有填满屏幕,所以我再次将videoPreviewLayer.frame = self.view.bounds 添加到viewDidLayoutSubviews() 以填充视图,因为我从笔尖设置了 Viewcontroller。它现在运行良好。谢谢
【解决方案2】:

这里是Victor Sigler's answer 更新到 Swift 4 没有强制解包、弱协议、在后台线程中执行昂贵的代码和其他改进。

注意AVCaptureMetadataOutputObjectsDelegate的方法从

captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!)

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

import UIKit
import AVFoundation

protocol BarcodeDelegate: class {
    func barcodeRead(barcode: String)
}

class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
    weak var delegate: BarcodeDelegate?

    var output = AVCaptureMetadataOutput()
    var previewLayer: AVCaptureVideoPreviewLayer!

    var captureSession = AVCaptureSession()

    override func viewDidLoad() {
        super.viewDidLoad()

        setupCamera()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        DispatchQueue.global(qos: .userInitiated).async {
            if !self.captureSession.isRunning {
                self.captureSession.startRunning()
            }
        }
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        DispatchQueue.global(qos: .userInitiated).async {
            if self.captureSession.isRunning {
                self.captureSession.stopRunning()
            }
        }
    }

    fileprivate func setupCamera() {
        guard let device = AVCaptureDevice.default(for: .video),
            let input = try? AVCaptureDeviceInput(device: device) else {
            return
        }

        DispatchQueue.global(qos: .userInitiated).async {
            if self.captureSession.canAddInput(input) {
                self.captureSession.addInput(input)
            }

            let metadataOutput = AVCaptureMetadataOutput()

            if self.captureSession.canAddOutput(metadataOutput) {
                self.captureSession.addOutput(metadataOutput)

                metadataOutput.setMetadataObjectsDelegate(self, queue: .global(qos: .userInitiated))

                if Set([.qr, .ean13]).isSubset(of: metadataOutput.availableMetadataObjectTypes) {
                    metadataOutput.metadataObjectTypes = [.qr, .ean13]
                }
            } else {
                print("Could not add metadata output")
            }

            self.previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
            self.previewLayer.videoGravity = .resizeAspectFill

            DispatchQueue.main.async {
                self.previewLayer.frame = self.view.bounds
                self.view.layer.addSublayer(self.previewLayer)
            }
        }
    }

    func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
        // This is the delegate's method that is called when a code is read
        for metadata in metadataObjects {
            if let readableObject = metadata as? AVMetadataMachineReadableCodeObject,
                let code = readableObject.stringValue {
                dismiss(animated: true)
                delegate?.barcodeRead(barcode: code)
                print(code)
            }
        }
    }
}

【讨论】:

  • 关于更改 AVCaptureMetadataOutputObjectsDelegate 的说明对我很有帮助,谢谢
【解决方案3】:

适用于所有代码类型的 Swift 4 中的条码扫描器

下面我想根据iOS条码扫描的一些想法来分享一下。

  • 将条码扫描器逻辑与视图逻辑分开,
  • 在 .plist 文件中添加条目
  • 设置exposurePointOfInterestfocusPointOfInterest
  • 使用正确转换的 CGRect 设置 rectOfInterests
  • 设置focusModeexposureMode
  • 在更改相机捕捉设置时正确使用 lockForConfiguration 锁定 captureDevice

在 .plist 文件中添加条目
在 Info.plist 文件中添加以下代码以允许您的应用程序访问 iPhone 的相机:

<key>NSCameraUsageDescription</key>
<string>Allow access to camera</string>

设置 exposurePointOfInterest 和 focusPointOfInterest
exposurePointOfInterestfocusPointOfInterest 允许更好的扫描质量,更快地将相机聚焦在屏幕中心点。

设置 rectOfInterests
这个属性让相机只聚焦在屏幕的一部分上。这样可以更快地扫描代码,只关注屏幕中心显示的代码 - 这是有用的,而其他代码很少在后台可用。

设置focusMode和exposureMode 属性应设置如下:

device.focusMode = .continuousAutoFocus
device.exposureMode = .continuousAutoExposure

这允许连续聚焦和设置曝光以适应扫描代码。

演示

在这里你可以找到实现这个想法的现成项目: https://github.com/lukszar/QuickScanner

【讨论】:

  • 我想要的完美演示。非常感谢
  • @HardikThakkar 很高兴听到这个消息
  • 要调整的属性的精彩总结,以获得更好的扫描仪性能。
【解决方案4】:

您需要将 NSCameraUsageDescription 添加到您的 Info.plist 文件中才能使其正常工作!

只需在 info.plist 中添加一行,然后在新创建的行中输入 NSCameraUsageDescription添加一个字符串 用于告知用户为什么需要在您的应用中访问相机。

这应该可以解决问题!

【讨论】:

  • 我加了!仍然无法正常工作,相机打开但没有检测到二维码或条形码
  • 是的,现在遇到了同样的问题!首先,我刚刚将提供的完整项目转换为 Swift 3 语法并添加了 NSCameraUsageDescription - 就像一个魅力一样工作。现在,在按照教程进行操作后,我遇到了同样的问题......目前还不确定交易是什么,但我会继续寻找。
【解决方案5】:
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
  print("caught QR code")
  for metadata in metadataObjects {
     let readableObject = metadata as! AVMetadataMachineReadableCodeObject
     let code = readableObject.stringValue
     if  code!.isEmpty {
        print("is empty")
     } else {
        self.captureSession.stopRunning()
        self.dismiss(animated: true, completion: nil)
        self.delegate?.gotQRCode(code: code!)
     }
  }
}

看起来方法的签名在 Swift 3 中发生了一些变化。这是正确的版本

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多