使用 AVFoudation 框架扫描 QR 码

在 iOS 7 之前,当你想要扫描 QR 码时,我们可能需要依赖第三方框架或库,如 zBarzXing 。但是 Apple 从 iOS 7 中引入了 AVCaptureMetaDataOutput 来读取条形码。

要使用 AVFoundation 读取 QR 码,我们需要设置/创建 AVCaptureSession 并使用 captureOutput:didOutputMetadataObjects:fromConnection:委托方法。

步骤 1

导入 AVFoundation 框架并确认 AVCaptureMetadataOutputObjectsDelegate 协议

import AVFoundation
class ViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate

第 2 步

QR 码读取完全基于视频捕获。因此,要捕获连续视频,请创建一个 AVCaptureSession 并设置设备输入和输出。在视图控制器 viewDidLoad 方法中添加以下代码

// Create an instance of the AVCaptureDevice and provide the video as the media type parameter.
let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
 
do {
    // Create an instance of the AVCaptureDeviceInput class using the device object and intialise capture session
    let input = try AVCaptureDeviceInput(device: captureDevice)
    captureSession = AVCaptureSession()
    captureSession?.addInput(input)
    
    // Create a instance of AVCaptureMetadataOutput object and set it as the output device the capture session.
    let captureMetadataOutput = AVCaptureMetadataOutput()
    captureSession?.addOutput(captureMetadataOutput)
    // Set delegate with a default dispatch queue
    captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
    //set meta data object type as QR code, here we can add more then one type as well 
    captureMetadataOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode]

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

    // Start capture session.
    captureSession?.startRunning()
} catch {
    // If any error occurs, let the user know. For the example purpose just print out the error
    print(error)
    return
}

第 3 步

实现 AVCaptureMetadataOutputObjectsDelegate 委托方法来读取 QR 码

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
    
    // Check if the metadataObjects array contains at least one object. If not no QR code is in our video capture
    if metadataObjects == nil || metadataObjects.count == 0 {
        // NO QR code is being detected.
        return
    }
    
    // Get the metadata object and cast it to `AVMetadataMachineReadableCodeObject`
    let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
    
    if metadataObj.type == AVMetadataObjectTypeQRCode {
        // If the found metadata is equal to the QR code metadata then get the string value from meta data
        let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj)
        
        if metadataObj.stringValue != nil {
           // metadataObj.stringValue is our QR code
        }
    }
} 

这里元数据对象也可以为你提供在相机源上读取的 QR 码的界限。要获取边界,只需将元数据对象传递给 videoPreviewLayertransformedMetadataObject 方法,如下所示。

let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj)
        qrCodeFrameView?.frame = barCodeObject!.bounds