Swift二维码,原理、生成、识别、读取、自定义、使用场景

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
查看查看682 回复回复1 收藏收藏 分享淘帖 转播转播 分享分享 微信
查看: 682|回复: 1
收起左侧

Swift二维码,原理、生成、识别、读取、自定义、使用场景

[复制链接]
cocoaswift 发表于 2016-6-13 17:04:47 | 显示全部楼层 |阅读模式
快来登录
获取最新的苹果动态资讯
收藏热门的iOS等技术干货
拷贝下载Swift Demo源代码

1、二维码简介


1.1、概念

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



1.2、二维码的使用场景

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



1.3、二维码生成方式

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


1.4、二维码读取

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


Swift二维码,原理、生成、识别、读取、自定义、使用场景

Swift二维码,原理、生成、识别、读取、自定义、使用场景 - 敏捷大拇指 - Swift二维码,原理、生成、识别、读取、自定义、使用场景





2、生成/识别/读取二维码



2.1、生成二维码


2.1.1、导入CoreImage框架

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


[Swift] 纯文本查看 复制代码
import CoreImage



2.1.2、通过滤镜CIFilter生成二维码

[Swift] 纯文本查看 复制代码
 /** 友情提示: 学习实用技术, 不要太在意语言, 把所有注意力, 放在步骤的实现上面 */
       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. 从滤镜里面取出结果图片
       // 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)

       // 4. 显示结果
       qrCodeImageView.image = image



2.1.3、自定义二维码

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


[Swift] 纯文本查看 复制代码
override func touchesBegan(touches: Set<UITouch>, 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.2、识别二维码


2.2.1、识别图片二维码

[Swift] 纯文本查看 复制代码
// 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)

          // 绘制识别到的二维码图片

      }

  }





2.3、扫描二维码


2.3.1、二维码扫描功能

[Swift] 纯文本查看 复制代码
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)
}



2.3.2、二维码边框描绘

[Swift] 纯文本查看 复制代码
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()
            }
        }

    }
}



2.3.3、二维码扫描区域限定

[Swift] 纯文本查看 复制代码
// 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)





3、使用注意

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






作者:大L君

都看到这里了,就把这篇资料推荐给您的好朋友吧,让他们也感受一下。

回帖是一种美德,也是对楼主发帖的尊重和支持。

*声明:敏捷大拇指是全球最大的Swift开发者社区、苹果粉丝家园、智能移动门户,所载内容仅限于传递更多最新信息,并不意味赞同其观点或证实其描述;内容仅供参考,并非绝对正确的建议。本站不对上述信息的真实性、合法性、完整性做出保证;转载请注明来源并加上本站链接,敏捷大拇指将保留所有法律权益。如有疑问或建议,邮件至marketing@swifthumb.com

*联系:微信公众平台:“swifthumb” / 腾讯微博:@swifthumb / 新浪微博:@swifthumb / 官方QQ一群:343549891(满) / 官方QQ二群:245285613 ,需要报上用户名才会被同意进群,请先注册敏捷大拇指

嗯,不错!期待更多好内容,支持一把:
支持敏捷大拇指,用支付宝支付10.24元 支持敏捷大拇指,用微信支付10.24元

评分

参与人数 1金钱 +10 贡献 +10 专家分 +10 收起 理由
Anewczs + 10 + 10 + 10 32个赞!专家给力!

查看全部评分

i0n1c 发表于 2016-6-14 02:05:02 | 显示全部楼层
8cuo~
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

做任务,领红包。
我要发帖

分享扩散

都看到这里了,就把这资料推荐给您的好朋友吧,让他们也感受一下。
您的每一位朋友访问此永久链接后,您都将获得相应的金钱积分奖励
热门推荐

合作伙伴

Swift小苹果

  • 北京治世天下科技有限公司
  • ©2014-2016 敏捷大拇指
  • 京ICP备14029482号
  • Powered by Discuz! X3.1 Licensed
  • swifthumb Wechat Code
  •   
快速回复 返回顶部 返回列表