以前就寫過二維碼掃描的文章鸵熟,今天難得抽出來時(shí)間重新整理了一下,把所有用都的關(guān)于二維碼的都寫在這了,二維碼問題揍鸟,看了這一篇文章就什么都解決了
原生二維碼掃描
個(gè)人是比較支持用原生二維碼掃描的,這里也就僅僅以原生二維碼掃面為范例句旱。另也有二維碼掃描庫ZBarSDK(點(diǎn)這里)和 ZXingObjC(點(diǎn)這里) 阳藻。
1.原生掃描用到的幾個(gè)類
@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里創(chuàng)建它們
// 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];
}
設(shè)置條碼類型
_output.metadataObjectTypes =@[AVMetadataObjectTypeQRCode];
添加掃描畫面
_preview =[AVCaptureVideoPreviewLayerlayerWithSession:_session];
_preview.videoGravity =AVLayerVideoGravityResizeAspectFill;
_preview.frame =self.view.layer.bounds;
[self.view.layerinsertSublayer:_previewatIndex:0];
開始掃描
[_sessionstartRunning];
最后實(shí)現(xiàn)協(xié)議AVCaptureMetadataOutputObjectsDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
NSString *stringValue;
if ([metadataObjectscount] >0){
//停止掃描
[_sessionstopRunning];
AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjectsobjectAtIndex:0];
stringValue = metadataObject.stringValue;
}
}
到了這一步就可以成功掃描二維碼了
還有一個(gè)小問題需要說明一下,之前寫過一篇限制二維碼掃描區(qū)域的谈撒,在這里稍微提一下腥泥。如下圖微信掃描,把掃描范圍限制在中間的方框內(nèi)
要想限制二維碼掃描區(qū)域啃匿,需要設(shè)置一個(gè)參數(shù)rectOfInterest 這個(gè)參數(shù)有點(diǎn)特別蛔外,這個(gè)參數(shù)的react 與平常設(shè)置的坐標(biāo)系是完全相反的,即X與Y互換溯乒、W與H互換冒萄。
PS: 有好多人問我上圖中方形的透明遮罩是怎么弄得,其實(shí)我在提供的demo里已經(jīng)加上了透明遮罩橙数,你可以直接下載demo去查看尊流。
附上demo地址:https://github.com/liutongchao/LCQRCodeUtil
設(shè)置的方法(掃描demo中已經(jīng)加入了設(shè)置掃描區(qū)域)
[_output setRectOfInterest:CGRectMake()];
如果你感到疑惑那么可以看一下我寫的這篇文章,里面有詳細(xì)研究灯帮。
iOS 原生二維碼掃描(可限制掃描區(qū)域)
生成二維碼圖片
生成二維碼和掃描二維碼圖片就比較簡單了崖技,自己也寫了個(gè)工具類來處理,這里直接貼出來代碼钟哥,后面也會有代碼可以下載
在這里借鑒的有別人的文章迎献,想看原文點(diǎn)這里
首先LCQrcodeUtil.h中
/**
* 生成二維碼圖片
*
* @param QRString 二維碼內(nèi)容
* @param sizeWidth 圖片size(正方形)
* @param color 填充色
*
* @return 二維碼圖片
*/
+(UIImage *)createQRimageString:(NSString *)QRString sizeWidth:(CGFloat)sizeWidth fillColor:(UIColor *)color;
LCQrcodeUtil.m中
#pragma mark 生成二維碼
/**
* 生成二維碼圖片
*
* @param QRString 二維碼內(nèi)容
* @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;
}
解析二維碼圖片
還是以源碼的形式貼出來
這里也有借鑒別人的代碼,點(diǎn)這里 查看原文
首先LCQrcodeUtil.h中
/**
* 讀取圖片中二維碼信息
*
* @param image 圖片
*
* @return 二維碼內(nèi)容
*/
+(NSString *)readQRCodeFromImage:(UIImage *)image;
LCQrcodeUtil.m中
#pragma mark 讀取圖片二維碼
/**
* 讀取圖片中二維碼信息
*
* @param image 圖片
*
* @return 二維碼內(nèi)容
*/
+(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;
}
}
都是直接封裝成類方法調(diào)用腻贰,這個(gè)類我也已經(jīng)上傳到git吁恍,不想CV的可以到git中下載,地址:
https://github.com/liutongchao/LCQRCodeUtil