AVFoundation二维码

有关二维码的介绍,我这里不做过多说明, 可以直接去基维百科查看,附上链接QR code.

IOS7之前,开发者进行扫码编程时,一般会借助第三方库。常用的是ZBarSDKaZXingObjC,IOS7之后,系统的AVMetadataObject类中,为我们提供了解析二维码的接口。经过测试,使用原生API扫描和处理的效率非常高,远远高于第三方库。


官方提供的接口非常简单,直接看代码,主要使用的是AVFoundation。

@interfaceViewController()//用于处理采集信息的代理


{

AVCaptureSession* session;//输入输出的中间桥梁

}

@end

@implementationViewController

- (void)viewDidLoad {

[superviewDidLoad];

// Do any additional setup after loading the view, typically from a nib.

//获取摄像设备

AVCaptureDevice* device = [AVCaptureDevicedefaultDeviceWithMediaType:AVMediaTypeVideo];

//创建输入流

AVCaptureDeviceInput* input = [AVCaptureDeviceInputdeviceInputWithDevice:device error:nil];

if(!input)return;

//创建输出流

AVCaptureMetadataOutput* output = [[AVCaptureMetadataOutputalloc]init];

//设置代理 在主线程里刷新

[output setMetadataObjectsDelegate:selfqueue:dispatch_get_main_queue()];

//设置有效扫描区域

CGRectscanCrop=[selfgetScanCrop:_scanWindow.bounds readerViewBounds:self.view.frame];

output.rectOfInterest = scanCrop;

//初始化链接对象

_session = [[AVCaptureSessionalloc]init];

//高质量采集率

[_session setSessionPreset:AVCaptureSessionPresetHigh];

[_session addInput:input];

[_session addOutput:output];

//设置扫码支持的编码格式(如下设置条形码和二维码兼容)

output.metadataObjectTypes=@[AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code,AVMetadataObjectTypeEAN8Code,AVMetadataObjectTypeCode128Code];

AVCaptureVideoPreviewLayer* layer = [AVCaptureVideoPreviewLayerlayerWithSession:_session];

layer.videoGravity=AVLayerVideoGravityResizeAspectFill;

layer.frame=self.view.layer.bounds;

[self.view.layer insertSublayer:layer atIndex:0];

//开始捕获

[_session startRunning];

}

-(void)captureOutput:(AVCaptureOutput*)captureOutput didOutputMetadataObjects:(NSArray*)metadataObjects fromConnection:(AVCaptureConnection*)connection{

if(metadataObjects.count>0) {

//[session stopRunning];

AVMetadataMachineReadableCodeObject* metadataObject = [metadataObjects objectAtIndex :0];

//输出扫描字符串

NSLog(@"%@",metadataObject.stringValue);

}

}




一些初始化的代码加上实现代理方法便完成了二维码扫描的工作,这里我们需要注意的是, 在二维码扫描的时候, 我们一般都会在屏幕中间放一个方框,用来显示二维码扫描的大小区间,这里我们在个AVCaptureMetadataOutput类中有一个rectOfInterest属性,它的作用就是设置扫描范围。

这个CGRect参数和普通的Rect范围不太一样,它的四个值的范围都是0-1,表示比例。

rectOfInterest都是按照横屏来计算的 所以当竖屏的情况下 x轴和y轴要交换一下。

宽度和高度设置的情况也是类似。

我们在上面设置有效扫描区域的方法如下


#pragma mark-> 获取扫描区域的比例关系

-(CGRect)getScanCrop:(CGRect)rect readerViewBounds:(CGRect)readerViewBounds

{

CGFloatx,y,width,height;

x = (CGRectGetHeight(readerViewBounds)-CGRectGetHeight(rect))/2/CGRectGetHeight(readerViewBounds);

y = (CGRectGetWidth(readerViewBounds)-CGRectGetWidth(rect))/2/CGRectGetWidth(readerViewBounds);

width =CGRectGetHeight(rect)/CGRectGetHeight(readerViewBounds);

height =CGRectGetWidth(rect)/CGRectGetWidth(readerViewBounds);

returnCGRectMake(x, y, width, height);

}



读取主要用到CoreImage 不过要强调的是读取二维码的功能只有在iOS8之后才支持,我们需要在相册中调用一个二维码,将其读取,代码如下

#pragma mark-> 我的相册

-(void)myAlbum{

NSLog(@"我的相册");

if([UIImagePickerControllerisSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]){

//1.初始化相册拾取器

UIImagePickerController*controller = [[UIImagePickerControlleralloc] init];

//2.设置代理

controller.delegate =self;

//3.设置资源:

/**

UIImagePickerControllerSourceTypePhotoLibrary,相册

UIImagePickerControllerSourceTypeCamera,相机

UIImagePickerControllerSourceTypeSavedPhotosAlbum,照片库

*/

controller.sourceType =UIImagePickerControllerSourceTypeSavedPhotosAlbum;

//4.随便给他一个转场动画

controller.modalTransitionStyle=UIModalTransitionStyleFlipHorizontal;

[selfpresentViewController:controller animated:YEScompletion:NULL];

}else{

UIAlertView* alert = [[UIAlertViewalloc]initWithTitle:@"提示"message:@"设备不支持访问相册,请在设置->隐私->照片中进行设置!"delegate:nilcancelButtonTitle:@"确定"otherButtonTitles:nil,nil];

[alert show];

}

}

完成相册代理, 我们在代理中添加读取二维码方法

#pragma mark-> imagePickerController delegate

- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info

{

//1.获取选择的图片

UIImage*image = info[UIImagePickerControllerOriginalImage];

//2.初始化一个监测器

CIDetector*detector = [CIDetectordetectorOfType:CIDetectorTypeQRCodecontext:niloptions:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}];

[picker dismissViewControllerAnimated:YEScompletion:^{

//监测到的结果数组

NSArray*features = [detector featuresInImage:[CIImageimageWithCGImage:image.CGImage]];

if(features.count >=1) {

/**结果对象 */

CIQRCodeFeature*feature = [features objectAtIndex:0];

NSString*scannedResult = feature.messageString;

UIAlertView* alertView = [[UIAlertViewalloc]initWithTitle:@"扫描结果"message:scannedResult delegate:nilcancelButtonTitle:@"确定"otherButtonTitles:nil,nil];

[alertView show];

}

else{

UIAlertView* alertView = [[UIAlertViewalloc]initWithTitle:@"提示"message:@"该图片没有包含一个二维码!"delegate:nilcancelButtonTitle:@"确定"otherButtonTitles:nil,nil];

[alertView show];

}

}];

}

因为没用真机,所以这里没有给出太多的截图, 用模拟器读取自带图片,结果如下


生成

生成二维码,其实也是用到CoreImage,但是步骤繁琐一些,代码如下


#pragma mark-> 二维码生成

-(void)create{

UIImage*image=[UIImageimageNamed:@"6824500_006_thumb.jpg"];

NSString*tempStr;

if(self.textField.text.length==0){

tempStr=@"ddddddddd";

}else{

tempStr=self.textField.text;

}

UIImage*tempImage=[QRCodeGenerator qrImageForString:tempStr imageSize:360Topimg:image withColor:RandomColor];

_outImageView.image=tempImage;

}

+(UIImage*)qrImageForString:(NSString*)string imageSize:(CGFloat)size Topimg:(UIImage*)topimg withColor:(UIColor*)color{

if(![string length]) {

returnnil;

}

QRcode *code = QRcode_encodeString([string UTF8String],0, QR_ECLEVEL_L, QR_MODE_8,1);

if(!code) {

returnnil;

}

// create context

CGColorSpaceRefcolorSpace =CGColorSpaceCreateDeviceRGB();

CGContextRefctx =CGBitmapContextCreate(0, size, size,8, size *4, colorSpace, kCGImageAlphaPremultipliedLast);

CGAffineTransformtranslateTransform =CGAffineTransformMakeTranslation(0, -size);

CGAffineTransformscaleTransform =CGAffineTransformMakeScale(1,-1);

CGContextConcatCTM(ctx,CGAffineTransformConcat(translateTransform, scaleTransform));

// draw QR on this context

[QRCodeGenerator drawQRCode:code context:ctx size:size withPointType:0withPositionType:0withColor:color];

// get image

CGImageRefqrCGImage =CGBitmapContextCreateImage(ctx);

UIImage* qrImage = [UIImageimageWithCGImage:qrCGImage];

if(topimg)

{

UIGraphicsBeginImageContext(qrImage.size);

//Draw image2

[qrImage drawInRect:CGRectMake(0,0, qrImage.size.width, qrImage.size.height)];

//Draw image1

floatr=qrImage.size.width*35/240;

[topimg drawInRect:CGRectMake((qrImage.size.width-r)/2, (qrImage.size.height-r)/2,r, r)];

qrImage=UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

}

// some releases

CGContextRelease(ctx);

CGImageRelease(qrCGImage);

CGColorSpaceRelease(colorSpace);

QRcode_free(code);

returnqrImage;

}

+ (void)drawQRCode:(QRcode *)code context:(CGContextRef)ctx size:(CGFloat)size withPointType:(QRPointType)pointType withPositionType:(QRPositionType)positionType withColor:(UIColor*)color {

unsignedchar*data =0;

intwidth;

data = code->data;

width = code->width;

floatzoom = (double)size / (code->width +2.0* qr_margin);

CGRectrectDraw =CGRectMake(0,0, zoom, zoom);

// draw

constCGFloat*components;

if(color) {

components =CGColorGetComponents(color.CGColor);

}else{

components =CGColorGetComponents([UIColorblackColor].CGColor);

}

CGContextSetRGBFillColor(ctx, components[0], components[1], components[2],1.0);

NSLog(@"aad :%f  bbd :%f  ccd:%f",components[0],components[1],components[2]);

for(inti =0; i < width; ++i) {

for(intj =0; j < width; ++j) {

if(*data &1) {

rectDraw.origin =CGPointMake((j + qr_margin) * zoom,(i + qr_margin) * zoom);

if(positionType == QRPositionNormal) {

switch(pointType) {

caseQRPointRect:

CGContextAddRect(ctx, rectDraw);

break;

caseQRPointRound:

CGContextAddEllipseInRect(ctx, rectDraw);

break;

default:

break;

}

}elseif(positionType == QRPositionRound) {

switch(pointType) {

caseQRPointRect:

CGContextAddRect(ctx, rectDraw);

break;

caseQRPointRound:

if((i>=0&& i<=6&& j>=0&& j<=6) || (i>=0&& i<=6&& j>=width-7-1&& j<=width-1) || (i>=width-7-1&& i<=width-1&& j>=0&& j<=6)) {

CGContextAddRect(ctx, rectDraw);

}else{

CGContextAddEllipseInRect(ctx, rectDraw);

}

break;

default:

break;

}

}

}

++data;

}

}

CGContextFillPath(ctx);

}


在textField输入,生成下图

长按二维码识别

这个功能有很多的地方在用, 最让人熟知的我想便是微信了,其实实现方法还是很简单的。

#pragma mark-> 长按识别二维码

-(void)dealLongPress:(UIGestureRecognizer*)gesture{

if(gesture.state==UIGestureRecognizerStateBegan){

_timer.fireDate=[NSDatedistantFuture];

UIImageView*tempImageView=(UIImageView*)gesture.view;

if(tempImageView.image){

//1. 初始化扫描仪,设置设别类型和识别质量

CIDetector*detector = [CIDetectordetectorOfType:CIDetectorTypeQRCodecontext:niloptions:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}];

//2. 扫描获取的特征组

NSArray*features = [detector featuresInImage:[CIImageimageWithCGImage:tempImageView.image.CGImage]];

//3. 获取扫描结果

CIQRCodeFeature*feature = [features objectAtIndex:0];

NSString*scannedResult = feature.messageString;

UIAlertView* alertView = [[UIAlertViewalloc]initWithTitle:@"扫描结果"message:scannedResult delegate:nilcancelButtonTitle:@"确定"otherButtonTitles:nil,nil];

[alertView show];

}else{

UIAlertView* alertView = [[UIAlertViewalloc]initWithTitle:@"扫描结果"message:@"您还没有生成二维码"delegate:nilcancelButtonTitle:@"确定"otherButtonTitles:nil,nil];

[alertView show];

}

}elseif(gesture.state==UIGestureRecognizerStateEnded){

_timer.fireDate=[NSDatedistantPast];

}

}

我们用刚才生成的二维码进行长按识别,效果如下

结语

本文demo下载地址请点这里Demo,

转自mokey1422所写的仿支付宝二维码。

系统原生的二维码扫描扫描识别速度,要比第三方好用得多,在没有特殊原因的情况下,比如7.0系统以下,我希望大家都能用系统原生的方法。

文章若有问题请给予指正,感谢。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • 在以前二维码比较知名的框架是ZXing和Zbar,现在,iOS系统提供了原生的实现二维码的功能 内容: 二维码的扫...
    零点知晨阅读 843评论 0 3
  • 之前使用过AVFoundation实现二维码扫描的功能,当初第一次接触这个框架,只是搜索资料,实现功能就草草了事了...
    月咏蝴蝶阅读 1,228评论 1 6
  • 空闲学习下AVFoundation中的AVCaptureSession,为了加深理解,写了一个识别二维码的demo...
    YxxxHao阅读 1,251评论 0 3
  • CIDetector 这个api是苹果在ios8之后提供的。所以用苹果自带的AVFundation扫描,如果从相册...
    然亦伞阅读 551评论 1 0
  • 原文:子曰:为政以德,譬如北辰,居其所,而众星共之。 孔子说:“以德行来治理国家,就像北极星一样安坐在自己的位置上...
    爱你and回忆阅读 167评论 0 0