风之力科技
股票代码:781340
400-718-5885或13605745691
service@nwpit.com

技术资讯

14Jun2016
点击

二维码 --智能时代的贴身小精灵

    你发现了吗?智能时代,到处都能用到扫一扫功能。餐厅,商超,娱乐场所,甚至银行都开启了扫码支付功能,通过扫码享受优惠折扣,轻松支付,何乐而不为呢。二维码作为一种身份识别广泛被印在产品包装、说明上,名片上,轻轻一扫就能详细了解对象信息。扫一扫我们就成了好友。那么今天我就来为大家细述一下二维码




二维码简介

  1. 概念
    • 二维码:是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;
    • 生成二维码:根据给定的信息, 将其按照二维码的编码方式生成一张图片
    • 读取二维码:识别二维码图像里面存储的数据


  2. 二维码的使用场景
    • 信息获取(名片、WIFI密码、资料)
    • 手机电商(用户扫码、手机直接购物下单)
    • 加好友(QQ, 微信, 扫一扫加好友)
    • 手机支付(扫描商品二维码,通过银行或第三方支付提供的手机端通道完成支付)


  3. 二维码生成方式
    • 从iOS7开始集成了二维码的生成和读取功能
    • 此前被广泛使用的zbarsdk目前不支持64位处理器,2015年2月1号起, 不允许不支持64位处理器的APP 上架


  4. 二维码读取
    • 直接从图片中识别,最低支持iOS8.0
    • 利用摄像头扫描识别,需要真机设备


生成/识别/读取二维码

  1. 生成二维码

    1. 导入CoreImage框架

      • 一些图片处理操作的功能, 都是用这个框架实现, 比如: 滤镜效果, 毛玻璃, 美颜相机....

        import CoreImage
    2. 通过滤镜CIFilter生成二维码





  1. 自定义二维码

    • 所谓自定义二维码, 就是指给二维码添加一些图片(前景或者背景图片), 或者改变下颜色
    • 可以添加前景图片的前提是因为二维码具备一定的"纠错率"
      • 如果二维码被部分遮挡, 可以根据其他部分, 计算出遮挡部分内容;
      • 但是要保证三个角不能被遮挡; 三个角用作扫描定位使用(可能用户倒着拍, 斜着拍等等)
    • 通过KVC 设置滤镜的 inputCorrectionLevel (纠错率)
      • @"L", @"M", @"Q", @"H" 中的一个
      • L水平 7%的字码可被修正
      • M水平 15%的字码可被修正
      • Q水平 25%的字码可被修正
      • H水平 30%的字码可被修正
    • 自定义二维码代码

      override func touchesBegan(touches: Set, withEvent event: UIEvent?) { view.endEditing(true) /** 友情提示: 学习实用技术, 不要太在意语言, 把所有注意力, 放在步骤的实现上面 */ let content = inputTV.text // 生成二维码 // 1. 创建二维码滤镜 let filter = CIFilter(name: "CIQRCodeGenerator") // 1.1 恢复滤镜默认设置 filter?.setDefaults() // 2. 设置滤镜的输入内容 // 通过KVC 给里面一个inputMessage 赋值 // 输入的内容类型一定是NSData let data = content.dataUsingEncoding(NSUTF8StringEncoding) filter?.setValue(data, forKey: "inputMessage") // 3.2 设置二维码纠错率 // 纠错率越高, 二维码图片,越复杂, 扫描识别的时间越长 filter?.setValue("M", forKey: "inputCorrectionLevel") // 3. 从滤镜里面取出结果图片 // 3.1 注意: 取出的图片是ciimage, 并且大小是23* 23 所以需要我们单独处理 // (23.0, 23.0) guard let outImage = filter?.outputImage else { return } // 3.1 图片处理 // 使用这种方式, 可以把图片放大处理, 而且保证不失真 let transform = CGAffineTransformMakeScale(20, 20) let resultImage = outImage.imageByApplyingTransform(transform) // 把CIImage转换成为UIImage let image = UIImage(CIImage: resultImage) print(image.size) // 3.3 自定义二维码 let center = UIImage(named: "erha.png") let hechengImage = createImage(image, centerImage: center!) // 4. 显示结果 qrCodeImageView.image = hechengImage } func createImage(sourceImage: UIImage, centerImage: UIImage) -> UIImage { let size = sourceImage.size // 1. 开启上下文 UIGraphicsBeginImageContext(size) // 2. 绘制大图片 sourceImage.drawInRect(CGRectMake(0, 0, size.width, size.height)) // 3. 绘制小图片 let w: CGFloat = 90 let h: CGFloat = 90 let x: CGFloat = (size.width - w) * 0.5 let y: CGFloat = (size.height - h) * 0.5 centerImage.drawInRect(CGRectMake(x, y, w, h)) // 4. 获取合成的图片 let resultImage = UIGraphicsGetImageFromCurrentImageContext() // 5. 关闭上下文 UIGraphicsEndImageContext() // 6. 返回结果 return resultImage }


  2. 识别二维码

    • 识别图片二维码

      // 1. 创建一个二维码探测器 let detector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy : CIDetectorAccuracyHigh]) // 2. 探测二维码图片的特征 guard let image = qrCodeImage.image else { return } let imageCI = CIImage(image: image) let features = detector.featuresInImage(imageCI!) // 3. 处理识别到的特征值 print(features) for feature in features { if feature.isKindOfClass(CIQRCodeFeature) { let qrCodeFeature = feature as! CIQRCodeFeature print(qrCodeFeature.messageString) // 绘制识别到的二维码图片 } }


    • 扫描二维码

      • 二维码扫描功能

        func scan() -> () { // 1. 获取摄像头设备 // 1.1 把摄像头当做一个输入设备 let device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo) var input: AVCaptureDeviceInput? do { input = try AVCaptureDeviceInput(device: device) }catch { print(error) return } // 2. 创建一个(元数据)输出处理对象 let output = AVCaptureMetadataOutput() // 2.1 设置代理, 拿到处理的结果 output.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue()) // 容错处理 // 如果已经添加过了, 就不会再次添加 if session.canAddInput(input) && session.canAddOutput(output) { session.addInput(input) session.addOutput(output) } // 设置元数据输出处理对象, 处理数据的类型 // 处理所有支持的类型 output.availableMetadataObjectTypes // 二维码 // 一定要注意 // 这行代码, 只能写在, 输出对象, 添加到会话之后 output.metadataObjectTypes = [AVMetadataObjectTypeQRCode] // 4. 启动会话(让输入开始采集数据, 让输出, 开始处理数据) session.startRunning() // 4.1 添加视频预览图层 // 可以让用户看到扫描的二维码 // 不是必须() let layer = AVCaptureVideoPreviewLayer(session: session) layer.frame = view.layer.bounds view.layer.insertSublayer(layer, atIndex: 0) }


      • 二维码边框描绘

        extension ScanVC: AVCaptureMetadataOutputObjectsDelegate { func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) { // 最后如果没有扫描到二维码内容的时候, 也会调用一次 removeQRCodeFrame() print(metadataObjects) for metaObj in metadataObjects { if metaObj.isKindOfClass(AVMetadataMachineReadableCodeObject) { // 就是把坐标转换成为, 在layer层上面的真实坐标 let transformObj = layer?.transformedMetadataObjectForMetadataObject(metaObj as! AVMetadataObject) let qrCodeObj = transformObj as! AVMetadataMachineReadableCodeObject // corners: 二维码的四个角 // 得到的结果, 是点对应的字典组成的数组 // 并且, 点坐标, 没法直接使用 // 需要借助layer, 进行转换成为, 我们可以直接处理的坐标 print(qrCodeObj.corners) drawQRCodeFrame(qrCodeObj) // stringValue: 二维码的具体内容 print(qrCodeObj.stringValue) } } } func drawQRCodeFrame(qrCodeObj: AVMetadataMachineReadableCodeObject) -> () { // 借助一个图形layer // 1. 创建形状图层 let shapLayer = CAShapeLayer() shapLayer.fillColor = UIColor.clearColor().CGColor shapLayer.strokeColor = UIColor.redColor().CGColor shapLayer.lineWidth = 6 // 2. 给layer, 设置一个形状路径, 让layer来展示 let corners = qrCodeObj.corners let path = UIBezierPath() var index = 0 for corner in corners { // { // X = "154.7997282646955"; // Y = "431.3352825435441"; // } var point = CGPointZero CGPointMakeWithDictionaryRepresentation((corner as! CFDictionary), &point) // 如果第一个点, 移动路径过去, 当做起点 if index == 0 { path.moveToPoint(point) }else { path.addLineToPoint(point) } // 如果不是第一个点, 添加一个线到这个点 index += 1 } path.closePath() // 2.1 根据四个角对应的坐标, 转换成为一个path // 2.2 给layer 的path 进行赋值 shapLayer.path = path.CGPath // 3. 添加形状图层, 到需要展示的图层上面 layer?.addSublayer(shapLayer) } func removeQRCodeFrame() -> () { guard let subLayers = layer?.sublayers else { return } for subLayer in subLayers { if subLayer.isKindOfClass(CAShapeLayer) { subLayer.removeFromSuperlayer() } } } }


      • 二维码扫描区域限定

        // 5. 设置输出的兴趣区域(限定扫描区域) // 注意事项: // 1. 里面的取值, 是一个0-->1的比例 // 2. 坐标相对应的坐标系是: 右上角为0, 0 (横屏状态下的坐标系) let size = UIScreen.mainScreen().bounds.size let x: CGFloat = backView.frame.origin.x / size.width let y: CGFloat = backView.frame.origin.y / size.height let w: CGFloat = backView.frame.size.width / size.width let h: CGFloat = backView.frame.size.height / size.height output.rectOfInterest = CGRectMake(y, x, h, w)


    • 使用注意
      • 读取二维码需要导入AVFoundation框架
      • 利用摄像头识别二维码中的内容(模拟器不行)