二維碼生成
//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~