美文网首页iOSiOS专题资源__系统知识点iOS图形处理相关
iOS 二维码扫描(你想要的都在这里了)

iOS 二维码扫描(你想要的都在这里了)

作者: WWest | 来源:发表于2016-05-05 17:57 被阅读25267次

以前就写过二维码扫描的文章,今天难得抽出来时间重新整理了一下,把所有用都的关于二维码的都写在这了,二维码问题,看了这一篇文章就什么都解决了

原生二维码扫描

个人是比较支持用原生二维码扫描的,这里也就仅仅以原生二维码扫面为范例。另也有二维码扫描库ZBarSDK(点这里) ZXingObjC(点这里)

1.原生扫描用到的几个类

@property (strong,nonatomic)AVCaptureDevice * device;
@property (strong,nonatomic)AVCaptureDeviceInput * input;
@property (strong,nonatomic)AVCaptureMetadataOutput * output;
@property (strong,nonatomic)AVCaptureSession * session;
@property (strong,nonatomic)AVCaptureVideoPreviewLayer * preview;

2.在viewDidLoad里创建它们

// Device
_device = [AVCaptureDevicedefaultDeviceWithMediaType:AVMediaTypeVideo];

// Input
_input = [AVCaptureDeviceInputdeviceInputWithDevice:self.deviceerror:nil];

// Output
_output = [[AVCaptureMetadataOutputalloc]init];
[_outputsetMetadataObjectsDelegate:selfqueue:dispatch_get_main_queue()];

// Session
_session = [[AVCaptureSessionalloc]init];
[_sessionsetSessionPreset:AVCaptureSessionPresetHigh];

连接输入和输出

if ([_sessioncanAddInput:self.input])
{
    [_sessionaddInput:self.input];
}

if ([_sessioncanAddOutput:self.output])
{
    [_sessionaddOutput:self.output];
}

设置条码类型

_output.metadataObjectTypes =@[AVMetadataObjectTypeQRCode];

添加扫描画面

_preview =[AVCaptureVideoPreviewLayerlayerWithSession:_session];
_preview.videoGravity =AVLayerVideoGravityResizeAspectFill;
_preview.frame =self.view.layer.bounds;
[self.view.layerinsertSublayer:_previewatIndex:0];

开始扫描

[_sessionstartRunning];

最后实现协议AVCaptureMetadataOutputObjectsDelegate

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
   NSString *stringValue;
   if ([metadataObjectscount] >0){
   //停止扫描
   [_sessionstopRunning];        
   AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjectsobjectAtIndex:0];
   stringValue = metadataObject.stringValue;        
  }
}

到了这一步就可以成功扫描二维码了

还有一个小问题需要说明一下,之前写过一篇限制二维码扫描区域的,在这里稍微提一下。如下图微信扫描,把扫描范围限制在中间的方框内

微信二维码扫描

要想限制二维码扫描区域,需要设置一个参数rectOfInterest 这个参数有点特别,这个参数的react 与平常设置的坐标系是完全相反的,即X与Y互换、W与H互换。

PS: 有好多人问我上图中方形的透明遮罩是怎么弄得,其实我在提供的demo里已经加上了透明遮罩,你可以直接下载demo去查看。
附上demo地址:https://github.com/liutongchao/LCQRCodeUtil

设置的方法(扫描demo中已经加入了设置扫描区域)

  [_output setRectOfInterest:CGRectMake()];

如果你感到疑惑那么可以看一下我写的这篇文章,里面有详细研究。

iOS 原生二维码扫描(可限制扫描区域)

生成二维码图片

生成二维码和扫描二维码图片就比较简单了,自己也写了个工具类来处理,这里直接贴出来代码,后面也会有代码可以下载

在这里借鉴的有别人的文章,想看原文点这里

首先LCQrcodeUtil.h中
/**
 *  生成二维码图片
 *
 *  @param QRString  二维码内容
 *  @param sizeWidth 图片size(正方形)
 *  @param color     填充色
 *
 *  @return  二维码图片
 */
+(UIImage *)createQRimageString:(NSString *)QRString sizeWidth:(CGFloat)sizeWidth fillColor:(UIColor *)color;
LCQrcodeUtil.m中
#pragma mark 生成二维码
/**
 *  生成二维码图片
 *
 *  @param QRString  二维码内容
 *  @param sizeWidth 图片size(正方形)
 *  @param color     填充色
 *
 *  @return  二维码图片
 */
+(UIImage *)createQRimageString:(NSString *)QRString sizeWidth:(CGFloat)sizeWidth fillColor:(UIColor *)color{
CIImage *ciimage = [self createQRForString:QRString];
UIImage *qrcode = [self createNonInterpolatedUIImageFormCIImage:ciimage withSize:sizeWidth];
if (color) {
    CGFloat R, G, B;
    
    CGColorRef colorRef = [color CGColor];
    long numComponents = CGColorGetNumberOfComponents(colorRef);
    
    if (numComponents == 4)
    {
        const CGFloat *components = CGColorGetComponents(colorRef);
        R = components[0];
        G = components[1];
        B = components[2];
    }
    
    UIImage *customQrcode = [self imageBlackToTransparent:qrcode withRed:R andGreen:G andBlue:B];
    return customQrcode;
}

return qrcode;

}
pragma mark
   #pragma mark - QRCodeGenerator
+ (CIImage *)createQRForString:(NSString *)qrString {
// Need to convert the string to a UTF-8 encoded NSData object
NSData *stringData = [qrString dataUsingEncoding:NSUTF8StringEncoding];
// Create the filter
CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
// Set the message content and error-correction level
[qrFilter setValue:stringData forKey:@"inputMessage"];
[qrFilter setValue:@"M" forKey:@"inputCorrectionLevel"];
// Send the image back
return qrFilter.outputImage;
}
pragma mark
#pragma mark - InterpolatedUIImage
+ (UIImage *)createNonInterpolatedUIImageFormCIImage:(CIImage *)image withSize:(CGFloat) size {
CGRect extent = CGRectIntegral(image.extent);
CGFloat scale = MIN(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent));
// create a bitmap image that we'll draw into a bitmap context at the desired size;
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);
// Create an image with the contents of our bitmap
CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
// Cleanup
CGContextRelease(bitmapRef);
CGImageRelease(bitmapImage);
return [UIImage imageWithCGImage:scaledImage];
}
pragma mark
#pragma mark - imageToTransparent
void ProviderReleaseData (void *info, const void *data, size_t size){
free((void*)data);
}
+ (UIImage*)imageBlackToTransparent:(UIImage*)image withRed:(CGFloat)red andGreen:(CGFloat)green andBlue:(CGFloat)blue{
const int imageWidth = image.size.width;
const int imageHeight = image.size.height;
size_t      bytesPerRow = imageWidth * 4;
uint32_t* rgbImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight);
// create context
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace,
                                             kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image.CGImage);
// traverse pixe
int pixelNum = imageWidth * imageHeight;
uint32_t* pCurPtr = rgbImageBuf;
for (int i = 0; i < pixelNum; i++, pCurPtr++){
    if ((*pCurPtr & 0xFFFFFF00) < 0x99999900){
        // change color
        uint8_t* ptr = (uint8_t*)pCurPtr;
        ptr[3] = red; //0~255
        ptr[2] = green;
        ptr[1] = blue;
    }else{
        uint8_t* ptr = (uint8_t*)pCurPtr;
        ptr[0] = 0;
    }
}
// context to image
CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow * imageHeight, ProviderReleaseData);
CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight, 8, 32, bytesPerRow, colorSpace,
                                    kCGImageAlphaLast | kCGBitmapByteOrder32Little, dataProvider,
                                    NULL, true, kCGRenderingIntentDefault);
CGDataProviderRelease(dataProvider);
UIImage* resultUIImage = [UIImage imageWithCGImage:imageRef];
// release
CGImageRelease(imageRef);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
return resultUIImage;
}

解析二维码图片

还是以源码的形式贴出来
这里也有借鉴别人的代码,点这里 查看原文

首先LCQrcodeUtil.h中
/**
 *  读取图片中二维码信息
 *
 *  @param image 图片
 *
 *  @return 二维码内容
 */
+(NSString *)readQRCodeFromImage:(UIImage *)image;
LCQrcodeUtil.m中
#pragma mark 读取图片二维码
/**
 *  读取图片中二维码信息
 *
 *  @param image 图片
 *
 *  @return 二维码内容
 */
+(NSString *)readQRCodeFromImage:(UIImage *)image{
NSData *data = UIImagePNGRepresentation(image);
CIImage *ciimage = [CIImage imageWithData:data];
if (ciimage) {
    CIDetector *qrDetector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:[CIContext contextWithOptions:@{kCIContextUseSoftwareRenderer:@(YES)}] options:@{CIDetectorAccuracy : CIDetectorAccuracyHigh}];
    NSArray *resultArr = [qrDetector featuresInImage:ciimage];
    if (resultArr.count >0) {
        CIFeature *feature = resultArr[0];
        CIQRCodeFeature *qrFeature = (CIQRCodeFeature *)feature;
        NSString *result = qrFeature.messageString;
        
        return result;
    }else{
        return nil;
    }
}else{
    return nil;
}

}

都是直接封装成类方法调用,这个类我也已经上传到git,不想CV的可以到git中下载,地址:
https://github.com/liutongchao/LCQRCodeUtil

有问题欢迎指正以及互相探讨 -- LC.West

相关文章

网友评论

  • 我是本人yyp:iOS8上CIDetector *qrDetector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:[CIContext contextWithOptions:@{kCIContextUseSoftwareRenderer:@(YES)}] options:@{CIDetectorAccuracy : CIDetectorAccuracyHigh}];
    这个qrDetector初始化为nil,请问有什么解决办法吗
  • edb493d9a752:关于扫描不清晰二维码 有什么想法吗 同一个二维码 微信 qq 可以轻松扫出来 但是调用系统的无法扫出(二维码不是很清晰) 扫描清晰二维码没有任何问题
    287450de448b:+1 同问
    有些二维码弄的很小 微信qq都可以秒扫出来 系统的扫了半天
    WWest:@ZeroDHero 目前没有
  • a群:在百度图片里,大部分 非黑白的个性化的二维码无法识别
    WWest:@a群 我试了一下没你说的那么严重,首先你要确定你扫的是不是一个二维码,其次你要看一下二维码是否被遮挡住了。二维码的识别跟是否是黑白的没有什么关系。
  • dispatch_async:读取图片二维码
    NSArray *resultArr = [qrDetector featuresInImage:ciimage];
    奔溃在这里...
    dispatch_async:@WWest 老铁 我自己的问题 昨天已经解决 不过还是要谢谢你:blush: :relaxed:
    WWest:报什么崩溃信息呢,贴一下
  • 杭子_:有个问题,跳转至二维码界面时,开始的卡顿现象如何优化?
    WWest:@杭子_ 这个应该是个视觉问题吧,不行的话就做一个动画修改透明度。
    杭子_:@WWest 这个看似卡顿现象好像 和 扫描定时器无关,就像平常 A push B跳转时,如果B没有设置self.view背景色,会出现这个卡顿现象(会有个黑色),但是如果设置self.view的背景色,并且alpha透明度是1的话,就没有这个现象啦。但是如果设置一个透明度的的话(比如0.6),就还会有看似卡顿现象。大神,你有过这种情况吗, 我这样说清楚吗
    WWest:如果是跳转的时候页面卡顿,那么就到页面显示出来的时候再启动扫描和定时器。
  • darkengine:到了iOS 11苹果直接弄了个AVCamBarcode,这样的话用原生的方案应该更简单了
  • 无意义的字符串:楼主你好,用这个demo跑起来我遇到一些问题,当我从上一级页面进入扫描页面时,无法隐藏tabbar
    WWest:能把你隐藏tabbar 的代码贴出来么?
  • 阳光的味道_丁达尔:按照楼主的方法写的 有的二维码的图片扫描不出来
    阳光的味道_丁达尔:@WWest 不好意思 是我选择的二维码的类型不对 添加上类型之后 就可以了
    WWest:@阳光的味道_丁达尔 应该不会啊,是不是二维码有问题?
  • 薛定谔的黑猫警长:读取本地二维码,每次都是在这儿闪退 NSArray *resultArr = [qrDetector featuresInImage:ciimage];
    薛定谔的黑猫警长:@WWest 现在解决了
    WWest:在扫描demo 中添加了扫描本地二维码的用例,你可以下载看一下
    WWest:我这边试着不会闪退啊,你的是哪个系统?
  • 王_a5ee:联系扫描两次怎么弄,每一次扫描之后,留在当前界面,第二次怎么也扫不出来
    WWest:再重新启动扫描和定时器就行了啊
  • 独木舟的木:请问扫描二维码成功之后播放的声音文件是 <AudioToolbox/AudioToolbox.h> 中的哪个?
    WWest:@独木舟的木 这个我不清楚,你需要搜索了解一下。
  • 浩青:楼主,你的git 吧[self setCropRect:kScanRect];
    [self performSelector:@selector(setupCamera) withObject:nil afterDelay:0.3];这写进viewWillAppear里 在pus进入这个控制器,手势侧滑不完全退出再回来会发现 扫描器不工作的。
    WWest:@浩青 这只是一个demo,具体的应用还要根据自己的情况来。
    浩青:没说清楚,[self setCropRect:kScanRect]; 这个方法调用的蒙版 在手势侧滑不完全退出再回来会发现越来越黑,[self performSelector:@selector(setupCamera) withObject:nil afterDelay:0.3];会导致扫描器不工作。不建议写到viewWillAppear
  • 执着于你莱:楼主是否尝试过当在进入扫描的时候程序挂起,我测试的时候,重新再走一次方法,会死掉,不知道怎么处理,我刚接触这个不久,请指教。
    WWest:@执着于你莱 看崩溃日志,查一下在哪崩的,一般就知道为什么崩了
  • crow226119:请问下楼主,微信是用的什么库?我们项目使用的zbar,但是扫描速度比微信慢很多。
    WWest:@crow226119 这个我就不知道喽。
  • MrHC:牛逼牛逼!感谢楼主的分享
  • a50735c6fbd5:很有用
  • 至恒之狐:我也用你这个相差不多的代码实现了扫描,但是控制 不释放,是什么原因导致的了
    WWest:这个问题太笼统,我也不知道怎么回答。文章里也只是部分代码,你可以下载demo看一下。
  • 鹰眼米霍克:mark一下 以后用到再来看下
  • 寸光片静:你好!试了下代码,但如果添加了那个扫描区域的话,就效果很差唉(就是扫描时间比较长,不然就是扫描出来),我用ipod试的,不知会会有影响呢!
    WWest:@寸光片静 好吧,其实倒是感觉没必要限制区域,更快的扫描到应该更好一点还。
    寸光片静:@WWest 不是,就是效果不好!感觉它的反应比较慢。如果不是位置问题,有看了你那个原生限制区域的那个了!
    WWest:@寸光片静 是不是区域设置错了呢,导致扫不出来。
  • d17711008314:正需要
  • 加拉隆的深渊之核:亲,怎么每次进去先是白色,在启动相机的?
    加拉隆的深渊之核:@WWest 好的,谢谢你哟
    WWest:@大白李 嗯,你可以把页面设置成黑色,这样就不太明显了
  • 魔魔571:赞一个
  • 韩大熊宝要姓张:二维码.不错
  • abner009007:顶一下
  • Link913:条形码可以扫吗
    WWest:@SkyHarute 可以的,需要设置扫码类型
  • RadioWaves:LZ很给力。加了qq给我做了指导。还顺便学了个定时器的问题。十分感谢
    WWest:@Cstye :beers:
  • RadioWaves:LZ你好
    很幸运看到了你的代码,我现在出了个问题,想请问一下,扫描完二维码之后我push到下一页控制器,但是 二维码还是在一直的扫描,下一页的控制器一直push过来。看log只要扫描完不及时离开二维码的话就会一直的不停的打log,我已经停止了session的扫描,并且将session制nil。但还是一直在扫描,请问您遇到过这个情况吗
    WWest:我运行了一下demo,扫描后直接就停止了啊 [_session stopRunning]; 有这一句,直接就停止了。
    RadioWaves:@WWest 好的。谢谢你
    WWest:@Cstye 你好,明天帮你看看。
  • 谢衣丶:想问一下。。能不能获取图片中二维码的定位块的坐标点啊@!!!
    谢衣丶:@WWest 谢谢!我用系统原生的框架找到了四个定位点坐标的属性
    WWest:@谢衣丶 这个还没研究过,不过你可以研究一下 AVMetadataMachineReadableCodeObject 的 corners 属性,这个好像跟定位有关。我更新了一下库,把这个属性给打印了一下。
  • 5f213eaf7185:你好,我的老是崩在这一句[_session addOutput:self.output];运行你的demo也是崩在这一句,是因为手机版本问题吗
    WWest:@5f213eaf7185 额,能说一下是什么原因不?
    WWest:@5f213eaf7185 手机版本是多少?是什么引起的崩溃?
    5f213eaf7185:@5f213eaf7185 感谢,弄好了已经
  • a421e6985b75:感谢楼主分享,不过楼主,在iOS10下,程序直接crash掉了,报了个EXC_BAD_ACCESS 错误,是苹果改了些什么吗,怎么解决呢
    WWest:@香了个蕉111 你好,已经修复,添加了iOS 10下相机使用权限。感谢你提出问题。
    WWest:@香了个蕉111 是扫描的demo么?
  • 40c6620494c9:那个ui 怎么画的。我擦。 我想不通怎么让中间透明。
    WWest:@偶像MJ QQ 413281269 你明天找我吧。
    40c6620494c9:@WWest 好。1561003235我的扣扣。或者你把你的给我,明天上班问你要。
    WWest:@偶像MJ 用的layer,我那有demo,明天发给你个。
  • WKCaesar:我怎么限制区域加不上去呢,设置为0.5,0.5,0.5,0.5,也看不到,有人说需要把设置限制区域的代码加到 NSNotificationCenter.defaultCenter().addObserverForName(AVCaptureInputPortFormatDescriptionDidChangeNotification, object: nil, queue: NSOperationQueue.currentQueue(), usingBlock: { [unowned self] _ in
    print("\(#function):: rectOfInterest")
    self.metadataOutput.rectOfInterest = CGRect(x: 0.5,y: 0.5,width: 0.5,height: 0.5)
    })
    我试了还是不行。。
    寸光片静:http://www.cnblogs.com/zhaopengtao14/p/3805602.html 这个可以看看!其实主要还是对象问题
    WWest:@颜如玉_黄金屋 我文章里有一个链接专门讲设置区域的,而且七楼评论里也有一个demo,你可以看一下。
    WWest:@颜如玉_黄金屋 怎么设置不上去,这个区域本来就是看不见的,加的那个扫描框只是一层layer。设置好扫描区域后,只有二维码到了那个区域才会被扫描到,而不是二维码一出现在屏幕中就会被识别。
  • 凯文Kevin21:设置条码类型

    _output.metadataObjectTypes =@[AVMetadataObjectTypeQRCode];
    这一行报错,楼主这个是什么原因呢。我是在真机上测试的,我查了一下这个是
    设置扫码支持的编码格式,
    我改成下面这样: _output.metadataObjectTypes=@[AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code,AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code];
    运行还是崩溃,,不知道是什么原因。
    凯文Kevin21:@就叫West怎么了 我是在9.3.2真机上跑的,。
    凯文Kevin21:@就叫West怎么了 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[AVCaptureMetadataOutput setMetadataObjectTypes:] - unsupported type found. Use -availableMetadataObjectTypes.'
    WWest:@七秒小鱼人_ 崩溃的信息是什么呢,引起崩溃的可能并不是设置扫描类型的代码。
  • 禾子_____: :smile: 支持
  • 相逢不晚为何匆匆:很好的文章
  • 430c9368aec4:我打开扫描想同时扫描好几个怎么做好。。。求大神帮助
    WWest:@wind_微笑 扫描的结果不是返回一个数组么,你试试同时扫两个是不是返回的数组里有两个?
  • 94fdc9682e01:非常好的文章,值得收藏
  • 9f94d02340f1:收藏先,指不定就用上了。
  • vernepung:https://github.com/vernepung/qrCodeDemo
    之前看你博客弄的,不过限制区域好像弄得不太对:stuck_out_tongue_winking_eye:没实际用到过就丢那了
    WWest:@vernepung 👌🏻,抽时间看一下
    vernepung:@就叫West怎么了 哈哈,可以帮忙改一下,刚又看你文章应该处理是稍微有点问题的
    WWest:@vernepung 感谢支持,你的demo我会下载看一下的,如果不错的话我会在文章里引用一下,正好缺一个demo:joy:
  • 至皓君:辛苦了,谢谢分享
    WWest:@至皓君 很高兴这篇文章能对你们有帮助:beers:
  • 73269e277f2b:不错,姿势一张
  • 莫_名:🏃🏻

本文标题:iOS 二维码扫描(你想要的都在这里了)

本文链接:https://www.haomeiwen.com/subject/tfvdrttx.html