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
}
guard let device = AVCaptureDevice.default(for: .video) else {
print("device error")
return
}
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
}
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)
Tips: 不能在主线程中扫描,否则会无法响应用户操作,导致卡死现象
DispatchQueue.global(qos: .userInitiated).async {
self.captureSession.startRunning()
}
遵循 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()
}
import Photos
Info.plist
文件里添加 Privacy - Photo Library Usage Description
描述申请访问相册权限。PHPhotoLibrary.authorizationStatus()
PHAuthorizationStatus
notDetermined 未申请
restricted 受限制
denied 已拒绝
authorized 已授权
limited 已授权有限库访问
PHPhotoLibrary.requestAuthorization { (status) in
// handle request result
}
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)
}
遵循 UIImagePickerControllerDelegate
和 UINavigationControllerDelegate
协议,并实现选中相片后触发的协议方法:
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)
}
导入: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二维码/条形码扫描+相册获取识别