iOS系统原生 二维码的生成、扫描和读取(高清、彩色)

摘要:
查找了一些资料博客啊发现,iOS7之前对于开发人员来说熟悉的第三方QRCode库有:ZXingGoogle出品并开源一直到现在都还有专人维护是世界上使用最广的二维码库iOS上比较稳定的移植版是ZXingObjZBar功能上与ZXing不相伯仲可惜的是项目在2012年之后就不维护了虽然代码到现在还可以使用但是iOS7之后呢,系统框架已经集成二维码的生成与读取,这使开发变得方便很多,并且会比第三方更加效率。

由于近期工作中遇到了个需求:需要将一些固定的字段 在多个移动端进行相互传输,所以就想到了 二维码 这个神奇的东东! 现在的大街上、连个摊煎饼的大妈 都有自己的二维码来让大家进行扫码支付。可见现在的二维码使用率多高,不光如此,在很多的社交类的APP 基本都有扫一扫加好友这个功能吧,因此决定学一学这个神奇的东西。

查找了一些资料博客啊发现,iOS7之前 对于开发人员来说 熟悉的第三方QRCode库有:


    • ZXing

      Google出品并开源 一直到现在都还有专人维护 是世界上使用最广的二维码库 iOS上比较稳定的移植版是ZXingObj

    • ZBar
      功能上与ZXing不相伯仲 可惜的是项目在2012年之后就不维护了 虽然代码到现在还可以使用

但是iOS7之后呢,系统框架已经集成二维码的生成与读取, 这使开发变得方便很多, 并且会比第三方更加效率。今天就来讲讲用系统原生的方式 来实现二维码的生成和扫描吧

( 一 )高清二维码

系统二维码主要通过 CIFilter 的对象来完成, 当然首先我们需要先导入这个类所在的框架,并实现下面的代码

   #import <CoreImage/CoreImage.h>

      // 1.创建过滤器 -- 苹果没有将这个字符定义为常量
     CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
      
      // 2.过滤器恢复默认设置
      [filter setDefaults];
     
     // 3.给过滤器添加数据(正则表达式/帐号和密码) -- 通过KVC设置过滤器,只能设置NSData类型
     NSString *dataString = @"http://www.baidu.com";
     NSData *data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
     [filter setValue:data forKeyPath:@"inputMessage"];
     
     // 4.获取输出的二维码
     CIImage *outputImage = [filter outputImage];
     
     // 5.显示二维码
     UIImage *image = [UIImage imageWithCIImage:outputImage];
iOS系统原生 二维码的生成、扫描和读取(高清、彩色)第1张
通过上面这种最简单的方式 生成的二维码很模糊,而且二维码的大小也不方便控制,

对于我们来说,需求的是一张 能控制大小,并且高清显示的二维码,因此我们需要用一种方式 将CIImage 转为我们心目中那个UIImage
/** 根据CIImage生成指定大小的UIImage */
+ (UIImage *)createNonInterpolatedUIImageFormCIImage:(CIImage *)image withSize:(CGFloat)size {
    CGRect extent = CGRectIntegral(image.extent);
    CGFloat scale = MIN(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent));
    
    // 1.创建bitmap;
    size_t width = CGRectGetWidth(extent) * scale;
    size_t height = CGRectGetHeight(extent) * scale;
    CGColorSpaceRef cs = CGColorSpaceCreateDeviceGray();
    CGContextRef bitmapRef = CGBitmapContextCreate(nil, width, height, 8, 0, cs, (CGBitmapInfo)kCGImageAlphaNone);
    CIContext *context = [CIContext contextWithOptions:nil];
    CGImageRef bitmapImage = [context createCGImage:image fromRect:extent];
    CGContextSetInterpolationQuality(bitmapRef, kCGInterpolationNone);
    CGContextScaleCTM(bitmapRef, scale, scale);
    CGContextDrawImage(bitmapRef, extent, bitmapImage);
    
    // 2.保存bitmap到图片
    CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
    CGContextRelease(bitmapRef);
    CGImageRelease(bitmapImage);
    return [UIImage imageWithCGImage:scaledImage];
}
iOS系统原生 二维码的生成、扫描和读取(高清、彩色)第2张

( 二 )彩色二维码

在使用过程中发现了个问题, 就是当我们使用一个长度过长的字段 去生成高清二维码的时候,这个二维码 就会变得非常密集、浓稠, 用手机来扫描的时候,由于手机摄像头像素问题很难读取到这个二维码,因此我需要一个 彩色二维码来增加它的辨识度。

    // 1、创建滤镜对象
    CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    
    // 恢复滤镜的默认属性
    [filter setDefaults];
    
    // 2、设置数据
    NSString *string_data = @"http://www.baidu.com";
    NSData *qrImageData = [string_data dataUsingEncoding:NSUTF8StringEncoding];
    
    // 设置过滤器的输入值, KVC赋值
    [filter setValue:qrImageData forKey:@"inputMessage"];
    
    // 3、获得滤镜输出的图像
    CIImage *outputImage = [filter outputImage];
    
    // 图片小于(27,27),我们需要放大
    outputImage = [outputImage imageByApplyingTransform:CGAffineTransformMakeScale(9, 9)];
    
    
    // 4、创建彩色过滤器(彩色的用的不多)
    CIFilter * color_filter = [CIFilter filterWithName:@"CIFalseColor"];
    
    // 设置默认值
    [color_filter setDefaults];
    
    // 5、KVC 给私有属性赋值
    [color_filter setValue:outputImage forKey:@"inputImage"];
    
    // 6、需要使用 CIColor 为背景颜色 和 主颜色 上色
    // inputColor0:背景颜色 ,inputColor1 主颜色
    // 注意不要使用 [CIColor redColor][CIColor blueColor],这些类似于UIColor的方法只有在iOS 10系统才有
    [color_filter setValue:[CIColor colorWithRed:1 green:1 blue:1] forKey:@"inputColor0"];
    [color_filter setValue:[CIColor colorWithRed:0 green:0 blue:1] forKey:@"inputColor1"];
    
    // 7、设置输出
    CIImage *colorImage = [color_filter outputImage];

    //8、输出UIImage
    UIImage *image = [UIImage imageWithCIIimage:colorImage];

扫描二维码

扫描主要使用的是AVFoundation使用起来也非常的简单 ,通过设置<AVCaptureMetadataOutputObjectsDelegate>代理可以监听扫描到的二维码中的信息

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>

@interface ViewController () <AVCaptureMetadataOutputObjectsDelegate>
/// 会话对象
@property (nonatomic, strong) AVCaptureSession *session;
/// 图层类
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
 // 1、获取摄像设备
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
    // 2、创建输入流
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
    
    // 3、创建输出流
    AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
    
    // 4、设置代理 在主线程里刷新
    [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    
    // 设置扫描范围(每一个取值0~1,以屏幕右上角为坐标原点)
    // 注:微信二维码的扫描范围是整个屏幕,这里并没有做处理(可不用设置)
    output.rectOfInterest = CGRectMake(0.05, 0.2, 0.7, 0.6);
    
    // 5、初始化链接对象(会话对象)
    self.session = [[AVCaptureSession alloc] init];
    // 高质量采集率
    [_session setSessionPreset:AVCaptureSessionPresetHigh];
    
    // 5.1 添加会话输入
    [_session addInput:input];
    
    // 5.2 添加会话输出
    [_session addOutput:output];
    
    // 6、设置输出数据类型,需要将元数据输出添加到会话后,才能指定元数据类型,否则会报错
    // 设置扫码支持的编码格式(如下设置条形码和二维码兼容)
    output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code,  AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code];
    
    // 7、实例化预览图层, 传递_session是为了告诉图层将来显示什么内容
    self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_session];
    _previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    _previewLayer.frame = self.view.layer.bounds;
    
    // 8、将图层插入当前视图
    [self.view.layer insertSublayer:_previewLayer atIndex:0];
    
    // 9、启动会话
    [_session startRunning];
}

#pragma mark - 获取扫描结果
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    if (metadataObjects.count > 0) {
        AVMetadataMachineReadableCodeObject *object = [metadataObjects lastObject];
        NSLog(@"%@", object.stringValue);
    }
}

@end

AVCaptureMetadataOutput有个属性rectOfInterest 他是用来控制你屏幕扫描的范围的,默认是按照整个屏幕来扫描,rectOfInterest的值的范围都是0-1 是按比例取值而不是实际尺寸 不过其实也很简单 只要换算一下就好了 ,这里唯一要注意的一点是 rectOfInterest 都是按照横屏来计算的 所以当竖屏的情况下 x轴和y轴要交换一下

读取二维码

读取主要用到CoreImage不过要强调的是读取二维码的功能只有在iOS8之后才支持,读取的代码也非常的简单

//首先拿到  我们需要读取的那个图片
UIImage * srcImage = qrcodeImage;

CIContext *context = [CIContext contextWithOptions:nil];

 // CIDetector(CIDetector可用于人脸识别)进行图片解析,声明一个CIDetector,并设定识别类型 CIDetectorTypeQRCode
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:context options:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}];

CIImage *image = [CIImage imageWithCGImage:srcImage.CGImage];

    // 取得识别结果是个数组
    NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];
    for (int index = 0; index < [features count]; index ++) {
        CIQRCodeFeature *feature = [features objectAtIndex:index];
        //这个String就是我们从二维码中获取到的信息
        NSString *scannedResult = feature.messageString;
}

免责声明:文章转载自《iOS系统原生 二维码的生成、扫描和读取(高清、彩色)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Redis hashjQueryEasyUI Messager基本使用下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

你注意到 .Net Framework 和 .Net Core 中使用 Session 的区别了吗?

起因 在测试一个例子时发现的问题,这个示例实现的功能是刷新页面也能保持表格锁定列的状态,先看下页面的完成效果: 测试中发现,几乎相同的代码: 在 FineUIMvc(Net Framework)下没有问题:http://mvc.fineui.com/#/GridLockColumn/SaveToDB 但是在 FineUICore(Net Core)下...

远程桌面mstsc应用

        当我们需要远程使用电脑或者服务器的时候,我们会选择使用远程桌面。但是,在多人使用远程桌面的时候。经常会遇到“客户端已达到最大连接数,请稍候再试”。原因解释:       如果是多个人管理的服务器的话,您可能会感觉这是很正常的现象。其实,则不然。以下将介绍Windows2003 Server中远程桌面的相关限制以及规定:           ...

ActiveMQ点对点的发送和接收

最近在工作中用到了ActiveMQ的点对点模式,在此用一个范例简单描述下使用过程。 1.首先在工程导入ActiveMQ的jar包,此jar包在下载的ActiveMQ资源里,这里用的是ActiveMQ版本是5.9。 2.编写消息发送者 package com.cool.jms; import org.apache.activemq.ActiveMQCon...

使用COOKIE实现登录 VS 使用SESSION实现登录

注:本文使用的代码基于PHP,其他语言逻辑同理。 一:使用COOKIE实现登录验证 使用cookie实现登录的方式,主要通过一些单向的加密信息进行验证。比如admin用户登录了之后,服务端生成一个cookie值:admin_1533006028_ bbf2c2b1ec5cfb62d0a30438d8d0305c ,这个cookie值包含用户名,cookie...

解决 "OperationalError: (sqlite3.OperationalError) no such table: ..."问题

参考:flask/sqlalchemy - OperationalError: (sqlite3.OperationalError) no such table 在用Flask写一个简单的py文件,做一个表单时,发现在login界面登陆的时候,出现: OperationalError: (sqlite3.OperationalError) no such...

ArcGis Server开发Web GIS新手体验(四)

接(三)这一节主要贴代码算了,通过代码看一下一些简单功能的实现。偶快要放假了,静不下心来写了,写得也不好。说不定贴代码效果更好。1、获取所有图层     ESRI.ArcGIS.Server.WebControls.WebMap webmap= Map1.CreateWebMap();    try    {     ESRI.ArcGIS.Carto...