swift - 二维码

kygz5479 8年前
   <h2>二维码简介</h2>    <ol>     <li>概念      <ul>       <li>二维码:是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;</li>       <li>生成二维码:根据给定的信息, 将其按照二维码的编码方式生成一张图片</li>       <li>读取二维码:识别二维码图像里面存储的数据</li>      </ul> </li>     <li>二维码的使用场景      <ul>       <li>信息获取(名片、WIFI密码、资料)</li>       <li>手机电商(用户扫码、手机直接购物下单)</li>       <li>加好友(QQ, 微信, 扫一扫加好友)</li>       <li>手机支付(扫描商品二维码,通过银行或第三方支付提供的手机端通道完成支付)</li>      </ul> </li>     <li>二维码生成方式      <ul>       <li>从iOS7开始集成了二维码的生成和读取功能</li>       <li>此前被广泛使用的zbarsdk目前不支持64位处理器,2015年2月1号起, 不允许不支持64位处理器的APP 上架</li>      </ul> </li>     <li>二维码读取      <ul>       <li>直接从图片中识别,最低支持iOS8.0</li>       <li>利用摄像头扫描识别,需要真机设备</li>      </ul> </li>    </ol>    <h2>生成/识别/读取二维码</h2>    <ol>     <li> <p>生成二维码</p>      <ol>       <li> <p>导入CoreImage框架</p>        <ul>         <li> <p>一些图片处理操作的功能, 都是用这个框架实现, 比如: 滤镜效果, 毛玻璃, 美颜相机....</p> <pre>  <code class="language-swift">import CoreImage</code></pre> </li>        </ul> </li>       <li> <p>通过滤镜CIFilter生成二维码</p> <pre>  <code class="language-swift"> /** 友情提示: 学习实用技术, 不要太在意语言, 把所有注意力, 放在步骤的实现上面 */</code></pre> </li>      </ol> </li>    </ol>    <pre>  <code class="language-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</code></pre>    <ol>     <li> <p>自定义二维码</p>      <ul>       <li>所谓自定义二维码, 就是指给二维码添加一些图片(前景或者背景图片), 或者改变下颜色</li>       <li>可以添加前景图片的前提是因为二维码具备一定的"纠错率"        <ul>         <li>如果二维码被部分遮挡, 可以根据其他部分, 计算出遮挡部分内容;</li>         <li>但是要保证三个角不能被遮挡; 三个角用作扫描定位使用(可能用户倒着拍, 斜着拍等等)</li>        </ul> </li>       <li>通过KVC 设置滤镜的 inputCorrectionLevel (纠错率)        <ul>         <li>@"L", @"M", @"Q", @"H" 中的一个</li>         <li>L水平 7%的字码可被修正</li>         <li>M水平 15%的字码可被修正</li>         <li>Q水平 25%的字码可被修正</li>         <li>H水平 30%的字码可被修正</li>        </ul> </li>       <li> <p>自定义二维码代码</p> <pre>  <code class="language-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    }</code></pre> </li>      </ul> </li>     <li> <p>识别二维码</p>      <ul>       <li> <p>识别图片二维码</p> <pre>  <code class="language-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)              // 绘制识别到的二维码图片          }      }</code></pre> </li>       <li> <p>扫描二维码</p>        <ul>         <li> <p>二维码扫描功能</p> <pre>  <code class="language-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)  }</code></pre> </li>         <li> <p>二维码边框描绘</p> <pre>  <code class="language-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()              }          }        }  }</code></pre> </li>         <li> <p>二维码扫描区域限定</p> <pre>  <code class="language-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)</code></pre> </li>        </ul> </li>       <li>使用注意        <ul>         <li><em>读取二维码需要导入AVFoundation框架</em></li>         <li><em>利用摄像头识别二维码中的内容(模拟器不行)</em></li>        </ul> </li>      </ul> </li>    </ol>    <p><br>  </p>    <p>文/<a href="/misc/goto?guid=4959674332762826839">大L君</a>(简书)</p>    <p> </p>