iOS_从相机或相册里扫描二维码或条形码


1. 从相机里扫描

1.1 申请相机权限

  • 导入: import AVFoundation
  • 在项目的 Info.plist 文件里添加 Privacy - Camera Usage Descriptio 描述申请使用相机权限。
  • 查询相机权限: AVCaptureDevice.authorizationStatus(for: .video)
  • 权限类型:AVAuthorizationStatus
notDetermined   未申请
restricted      受限制
denied          已拒绝
authorized      已授权
  • 请求相机权限,系统弹出授权申请提示:
AVCaptureDevice.requestAccess(for: .video) { (status) in
    // handle request result
}

1.2 创建Scanner

  • 获得设备:
 guard let device = AVCaptureDevice.default(for: .video) else {
    print("device error")
    return
}
  • 创建input:
 let input: AVCaptureDeviceInput
do {
  input = try AVCaptureDeviceInput(device: device)
} catch {
  print("input error")
  return
}
if self.captureSession.canAddInput(input) {
    self.captureSession.addInput(input)
} else {
    print("session can't add input")
    return
}
  • 创建 output:
let output = AVCaptureMetadataOutput()
if self.captureSession.canAddOutput(output) {
    // Tips: add output must before of set output
    self.captureSession.addOutput(output)
} else {
    print("session can't add output")
    return
}

// Set metadata identification type qr: QR code; Other: Barcode
// 设置扫描类型(qr:二维码,其他:条形码)
let hopeSupportTypes = [AVMetadataObject.ObjectType.qr,
                        AVMetadataObject.ObjectType.ean13,
                        AVMetadataObject.ObjectType.ean8,
                        AVMetadataObject.ObjectType.pdf417]
var types: [AVMetadataObject.ObjectType] = []
for type in hopeSupportTypes {
    if output.availableMetadataObjectTypes.contains(type) {
        types.append(type)
    }
}
output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
output.metadataObjectTypes = types        
output.rectOfInterest = CGRect(x: 0, y: 0,
                               width: self.view.bounds.size.width,
                               height: self.view.bounds.size.height)
  • 设置预览视图:
let previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
previewLayer.frame = scanView.layer.bounds
previewLayer.videoGravity = .resizeAspectFill
scanView.layer.addSublayer(previewLayer)

1.3 开始扫描

Tips: 不能在主线程中扫描,否则会无法响应用户操作,导致卡死现象

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

1.4 处理扫描结果

遵循 AVCaptureMetadataOutputObjectsDelegate 协议,实现扫描回调方法:

 func metadataOutput(_ output: AVCaptureMetadataOutput,
                    didOutput metadataObjects: [AVMetadataObject],
                    from connection: AVCaptureConnection) {
    guard let metadataObject = metadataObjects.first else {
        captureSession.stopRunning()
        return
    }
    guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else {
        print("as? AVMetadataMachineReadableCodeObject faliue")
        return
    }
    guard let stringValue = readableObject.stringValue else {
        print("stringValue faliue")
        return
    }
    AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
    print("scan result: \(stringValue)")   // print result
    captureSession.stopRunning()
}

2. 从相册里扫描

2.1 获取相册权限

  • 导入: import Photos
  • 在项目的 Info.plist 文件里添加 Privacy - Photo Library Usage Description 描述申请访问相册权限。
  • 查询相机权限:PHPhotoLibrary.authorizationStatus()
  • 权限类型:PHAuthorizationStatus
notDetermined   未申请
restricted      受限制
denied          已拒绝
authorized      已授权
limited         已授权有限库访问
  • 请求相册权限,系统弹出授权申请提示:
PHPhotoLibrary.requestAuthorization { (status) in
    // handle request result
}

2.2 打开相册

Tips: 打开相册必须在主线程中执行

 /// have photos permission
DispatchQueue.main.async {
    self.openPhotoLabrary()
}

private func openPhotoLabrary() {
    let picker = UIImagePickerController()
    picker.title = "Photos"
    picker.delegate = self
    picker.allowsEditing = true
    picker.sourceType = .photoLibrary
    picker.navigationBar.barStyle = .default
    self.present(picker, animated: true, completion: nil)
}

2.3 获得选择结果

遵循 UIImagePickerControllerDelegateUINavigationControllerDelegate 协议,并实现选中相片后触发的协议方法:

func imagePickerController(_ picker: UIImagePickerController,
                           didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    dismiss(animated: true)
    
    guard let image = info[.originalImage] as? UIImage else {
        print("choose not image")
        return
    }
    parseBarCode(image: image)
}

2.4 解析相片中的二维码或条形码

导入:import Vision

 /// parse qrCode or barCode
private func parseBarCode(image: UIImage) {
    guard let cgimg = image.cgImage else {
        return
    }
    
    let request = VNDetectBarcodesRequest { req, err in
        if let error = err {
            print("parseBarCode error: \(error)")
            return
        }
        self.handleResults(req.results)
    }
    
    let handler = VNImageRequestHandler(cgImage: cgimg)
    do {
        try handler.perform([request])
    } catch {
        print("parseBarCode error: \(error)")
    }
}

private func handleResults(_ result: [VNObservation]?) {
    guard let results = result, results.count > 0 else {
        print("parseBarCode result is nil: \(String(describing: result))")
        return
    }
    for result in results {
        self.handleResult(result)
    }
}

private func handleResult(_ result: VNObservation) {
    guard let barcode = result as? VNBarcodeObservation,
          let value = barcode.payloadStringValue else {
        print("handleResult covert to string error: \(result)")
        return
    }
    
    if barcode.symbology == .qr {
        print("二维码: \(value)")
    } else {
        print("条形码: \(value), \(barcode.symbology.rawValue)")
    }
}

github demo


参考:
iOS16 Swift二维码/条形码扫描+相册获取识别

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,以下是一个简单的 iOS 扫描二维码的实现: 1. 导入 AVFoundation 框架到你的项目中 2. 创建一个 UIView 用于显示摄像头捕获到的图像,并添加到视图中 3. 创建一个 AVCaptureSession 对象,设置采集质量和设备(摄像头),并将 AVCaptureDeviceInput 对象添加到会话中 4. 创建一个 AVCaptureMetadataOutput 对象,并将其添加到会话中,设置输出代理,并添加需要扫描的码类型 5. 创建一个 AVCaptureVideoPreviewLayer 对象,并将其添加到 UIView 上,用于显示摄像头捕获到的图像 6. 调用 AVCaptureSession 的 startRunning 方法,开始捕获摄像头数据 7. 实现 AVCaptureMetadataOutputObjectsDelegate 协议中的方法,获取扫描到的二维码信息 下面是一份示例代码,你可以根据自己的需求进行修改和完善: ``` // 创建 UIView 用于显示摄像头捕获到的图像 UIView *previewView = [[UIView alloc] initWithFrame:self.view.bounds]; [self.view addSubview:previewView]; // 创建 AVCaptureSession 对象 AVCaptureSession *session = [[AVCaptureSession alloc] init]; session.sessionPreset = AVCaptureSessionPresetHigh; // 获取设备(摄像头) AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; // 创建 AVCaptureDeviceInput 对象 NSError *error = nil; AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; if (input) { // 添加 AVCaptureDeviceInput 对象到会话中 [session addInput:input]; // 创建 AVCaptureMetadataOutput 对象 AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init]; // 添加 AVCaptureMetadataOutput 对象到会话中 [session addOutput:output]; // 设置输出代理,在主线程中执行 dispatch_queue_t mainQueue = dispatch_get_main_queue(); [output setMetadataObjectsDelegate:self queue:mainQueue]; // 添加需要扫描的码类型 NSArray *codeTypes = @[AVMetadataObjectTypeQRCode]; [output setMetadataObjectTypes:codeTypes]; // 创建 AVCaptureVideoPreviewLayer 对象 AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session]; [previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill]; // 设置 AVCaptureVideoPreviewLayer 的 frame,并添加到 UIView 上 [previewLayer setFrame:previewView.layer.bounds]; [previewView.layer addSublayer:previewLayer]; // 开始捕获摄像头数据 [session startRunning]; } else { NSLog(@"Error: %@", error); } // 实现 AVCaptureMetadataOutputObjectsDelegate 协议中的方法,获取扫描到的二维码信息 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { if (metadataObjects != nil && [metadataObjects count] > 0) { // 获取扫描到的二维码信息 AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0]; if ([[metadataObj type] isEqualToString:AVMetadataObjectTypeQRCode]) { NSString *qrCode = [metadataObj stringValue]; NSLog(@"Scanned QR Code: %@", qrCode); } } } ``` 希望这个简单的示例能够帮助到你。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫小言mo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值