Swift3.0生成二维码、扫描二维码、相册读取二维码,兼容iOS7(结合ZXingObjC)
二维码生成
//MARK: 传进去字符串,生成二维码图片(>=iOS7) text:要生成的二维码内容 WH:二维码高宽 private func creatQRCodeImage(text: String,WH:CGFloat) -> UIImage{ //创建滤镜 let filter = CIFilter(name: "CIQRCodeGenerator") //还原滤镜的默认属性 filter?.setDefaults() //设置需要生成二维码的数据 filter?.setValue(text.data(using: String.Encoding.utf8), forKey: "inputMessage") //从滤镜中取出生成的图片 let ciImage = filter?.outputImage //这个清晰度好 let bgImage = createNonInterpolatedUIImageFormCIImage(image: ciImage!, size: WH) return bgImage }
上面生成的image,需要用到一个方法,原因是直接生产的图片二维码清晰度不够,需要处理一下
//MARK: - 根据CIImage生成指定大小的高清UIImage private func createNonInterpolatedUIImageFormCIImage(image: CIImage, size: CGFloat) -> UIImage { let extent: CGRect = image.extent.integral let scale: CGFloat = min(size/extent.width, size/extent.height) let width = extent.width * scale let height = extent.height * scale let cs: CGColorSpace = CGColorSpaceCreateDeviceGray() let bitmapRef = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: cs, bitmapInfo: 0)! let context = CIContext(options: nil) let bitmapImage: CGImage = context.createCGImage(image, from: extent)! bitmapRef.interpolationQuality = CGInterpolationQuality.none bitmapRef.scaleBy(x: scale, y: scale) bitmapRef.draw(bitmapImage, in: extent) let scaledImage: CGImage = bitmapRef.makeImage()! return UIImage(cgImage: scaledImage) }
这样,我们就能得到想要的二维码图片了
有时,我们需要在二维码中间添加log水印等,我试过两种方法,第一种是直接用图形上下文UIGraphicsBeginImageContext这种实现,不够实现起来有点模糊,后来就直接用最原始方式了:直接add图片上去(也可能是我第一种实现有问题,这里大家可以自己试一下先)
添加log:(一些参数可以自己调,这里做个示例)
//MARK: - 根据背景图片和头像合成头像二维码 private func creatIconImage(iconImage:UIImage,sizeWH:CGFloat,superImgView:UIImageView){ let iconImgView = UIImageView(image: iconImage) iconImgView.contentMode = .scaleAspectFit iconImgView.frame = CGRect(x: (superImgView.bounds.size.width-sizeWH)/2, y: (superImgView.bounds.size.height-sizeWH)/2, width: sizeWH, height: sizeWH) iconImgView.layer.cornerRadius = 5 iconImgView.layer.borderColor = UIColor.white.cgColor iconImgView.layer.borderWidth = 4 iconImgView.layer.masksToBounds = true superImgView.addSubview(iconImgView) }
二维码扫描
使用AVCaptureDevice,基于系统的AVFoundation框架,所以使用前,先import
import UIKit
import AVFoundation
1、定义扫描的一些属性
//扫描定义属性 var device:AVCaptureDevice! = nil var input:AVCaptureDeviceInput! = nil var output:AVCaptureMetadataOutput! = nil var session:AVCaptureSession! = nil var preview:AVCaptureVideoPreviewLayer! = nil
2、设置扫描
/// 设置扫描参数 func setupCamera() { DispatchQueue.global().async { if (self.device == nil){ self.device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) do{ self.input = try AVCaptureDeviceInput.init(device: self.device) }catch{ print("self.input init error") } self.output = AVCaptureMetadataOutput.init() self.output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) self.session = AVCaptureSession.init() self.session.canSetSessionPreset(AVCaptureSessionPresetHigh) if self.session.canAddInput(self.input){ self.session .addInput(self.input) self.canOpen = true }else{ DispatchQueue.main.async { let alert = UIAlertView(title: "提示", message: "打开相机权限", delegate: self, cancelButtonTitle: "取消", otherButtonTitles: "设置") alert.show() } } if self.canOpen{ if self.session.canAddOutput(self.output){ self.session.addOutput(self.output) } // 只支持二维码 self.output.metadataObjectTypes = [AVMetadataObjectTypeQRCode] self.preview = AVCaptureVideoPreviewLayer(session: self.session) self.preview.videoGravity = AVLayerVideoGravityResizeAspectFill DispatchQueue.main.async { self.preview.frame = CGRect(x: 0, y: 0, width: KScreenWidth, height: KScreenHeight) self.view.layer.insertSublayer(self.preview, at: 0) } } } if self.canOpen{ DispatchQueue.main.async { //开启定时器,构造移动动画效果 self.timer = Timer(timeInterval: 0.02, target: self, selector: #selector(self.lineAnimation), userInfo: nil, repeats: true) RunLoop.current.add(self.timer!, forMode: .defaultRunLoopMode) //开始采集数据 self.session.startRunning() } } } }
扫描后,会触发一个代理,这里我们可以获取到扫描内容
// MARK: - 扫描二维码后的代理 extension QRCodeScanViewController:AVCaptureMetadataOutputObjectsDelegate{ func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) { var strValue:String = "" if metadataObjects.count>0{ let obj:AVMetadataMachineReadableCodeObject = metadataObjects.first as! AVMetadataMachineReadableCodeObject strValue = obj.stringValue } self.session.stopRunning() self.timer?.invalidate() self.timer = nil self.playBeep() self.showQRCode(qrcodeString: strValue) } }
以上都是一些核心代码,当然,我们还需要对它进行一些UI自定义,比如扫描背景色,扫描提示说明,红线的动画移动等
这里我就不一一贴出来了,文章最后会有获取源码的链接地址。
其实到这里,我们都可以依靠系统提供的API实现二维码的生成和扫描功能,最低要求的系统为iOS7。
下面,如果是从相册获取呢?
二维码相册读取
ios从相册读取二维码,在ios8以上,苹果依然提供了自带的识别图片二维码的功能,这种方式效率最好,也是最推荐的,但在兼容ios7时,我们就必须用其他方式实现。
选择第三方框架:
ZXingObjC OR ZBar ??
这里我选择的是 ZXingObjC,相比于ZBar,这个库一直有人在维护,而且易用性相比后者 也好一点(个人觉得哈,当然也有很多人选择的ZBar,其实都差不多)。
1、pod导入ZXingObjc
pod 'ZXingObjC', '~> 3.0'
代码实现记录如下:
注意:这里我之前测试的代码是用oc写的,还没来得及转成swift3,大家先看思路哈,勿怪~~
2、从相册选择好图片
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{ UIImage *image = [info objectForKey:@"UIImagePickerControllerOriginalImage"]; [self dismissViewControllerAnimated:YES completion:^{ [self getInfoWithImage:image]; }]; }
3、解析图片二维码-(void)getInfoWithImage:(UIImage *)image{
//8系统以上用系统提供的方法 if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")){ CIDetector*detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{ CIDetectorAccuracy : CIDetectorAccuracyHigh }]; NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]]; if (features.count >= 1){ for (int index = 0; index < [features count]; index ++) { CIQRCodeFeature *feature = [features objectAtIndex:index]; NSString *scannedResult = feature.messageString; NSLog(@"result:%@",scannedResult); //进行自己业务处理 } }else{ NSLog(@"no image"); } }else{ NSLog(@"ios8 以下系统"); CGImageRef imageToDecode=[image CGImage]; ZXLuminanceSource * source = [[ZXCGImageLuminanceSource alloc] initWithCGImage:imageToDecode]; ZXBinaryBitmap * bitmap = [ZXBinaryBitmap binaryBitmapWithBinarizer:[ZXHybridBinarizer binarizerWithSource:source]]; NSError *error = nil; ZXDecodeHints *hints = [ZXDecodeHints hints]; ZXMultiFormatReader * reader = [ZXMultiFormatReader reader]; ZXResult *result = [reader decode:bitmap hints:hints error:&error]; if (result) { NSString *contents = result.text; NSLog(@"解析成功:%@",contents); //进行自己业务处理 }else{ NSLog(@" --- 解析失败"); }
}
}
系统 CIDetector识别二维码,我后面加了swift4版本写法,供参考
/// 识别图片二维码,要求ios8系统及以上 /// /// - Parameter img: <#img description#> func getQRCodeWithImage(img:UIImage) { let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh]) let features = detector?.features(in: CIImage(cgImage: img.cgImage!)) if features == nil || features!.count <= 0{ return } for feature in features! { if let qrcode = feature as? CIQRCodeFeature{ self.playBeep() print(qrcode.messageString) } } }
最后,获取源码地址
Enjoy~