iOS自己实现二维码生成与扫描

来源:互联网 发布:公司淘宝培训协议书 编辑:程序博客网 时间:2024/05/24 16:13

二维码的生成

 + (UIImage *)qrImageForString:(NSString *)string imageSize:(CGFloat)Imagesize logoImageSize:(CGFloat)waterImagesize{CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];[filter setDefaults];NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];[filter setValue:data forKey:@"inputMessage"];//通过kvo方式给一个字符串,生成二维码[filter setValue:@"H" forKey:@"inputCorrectionLevel"];//设置二维码的纠错水平,越高纠错水平越高,可以污损的范围越大CIImage *outPutImage = [filter outputImage];//拿到二维码图片return [[self alloc] createNonInterpolatedUIImageFormCIImage:outPutImage withSize:Imagesize waterImageSize:waterImagesize];}- (UIImage *)createNonInterpolatedUIImageFormCIImage:(CIImage *)image withSize:(CGFloat) size waterImageSize:(CGFloat)waterImagesize{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;//创建一个DeviceGray颜色空间CGColorSpaceRef cs = CGColorSpaceCreateDeviceGray();//CGBitmapContextCreate(void * _Nullable data, size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow, CGColorSpaceRef  _Nullable space, uint32_t bitmapInfo)//width:图片宽度像素//height:图片高度像素//bitsPerComponent:每个颜色的比特值,例如在rgba-32模式下为8//bitmapInfo:指定的位图应该包含一个alpha通道。CGContextRef bitmapRef = CGBitmapContextCreate(nil, width, height, 8, 0, cs, (CGBitmapInfo)kCGImageAlphaNone);CIContext *context = [CIContext contextWithOptions:nil];//创建CoreGraphics imageCGImageRef 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);//原图UIImage *outputImage = [UIImage imageWithCGImage:scaledImage];//给二维码加 logo 图UIGraphicsBeginImageContextWithOptions(outputImage.size, NO, [[UIScreen mainScreen] scale]);[outputImage drawInRect:CGRectMake(0,0 , size, size)];//logo图UIImage *waterimage = [UIImage imageNamed:@"icon_imgApp"];//把logo图画到生成的二维码图片上,注意尺寸不要太大(最大不超过二维码图片的%30),太大会造成扫不出来[waterimage drawInRect:CGRectMake((size-waterImagesize)/2.0, (size-waterImagesize)/2.0, waterImagesize, waterImagesize)];UIImage *newPic = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();return newPic;}

修改二维码的颜色

- (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);CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image.CGImage); // 遍历像素int pixelNum = imageWidth * imageHeight;uint32_t* pCurPtr = rgbImageBuf;for (int i = 0; i < pixelNum; i++, pCurPtr++){if ((*pCurPtr & 0xFFFFFF00) < 0x99999900) // 将白色变成透明{// 改成下面的代码,会将图片转成想要的颜色uint8_t* ptr = (uint8_t*)pCurPtr;ptr[3] = red; //0~255ptr[2] = green;ptr[1] = blue;} else {uint8_t* ptr = (uint8_t*)pCurPtr;ptr[0] = 0;}}// 输出图片CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow * imageHeight, nil);CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight, 8, 32, bytesPerRow, colorSpace, kCGImageAlphaLast | kCGBitmapByteOrder32Little, dataProvider, NULL, true, kCGRenderingIntentDefault);CGDataProviderRelease(dataProvider);UIImage* resultUIImage = [UIImage imageWithCGImage:imageRef]; // 清理空间CGImageRelease(imageRef);CGContextRelease(context);CGColorSpaceRelease(colorSpace);return resultUIImage;}

扫码

@interface ScanQRViewController ()//捕获设备,通常是前置摄像头,后置摄像头,麦克风(音频输入)@property(nonatomic)AVCaptureDevice *device;//AVCaptureDeviceInput 代表输入设备,他使用AVCaptureDevice 来初始化@property(nonatomic)AVCaptureDeviceInput *input;//设置输出类型为Metadata,因为这种输出类型中可以设置扫描的类型,譬如二维码//当启动摄像头开始捕获输入时,如果输入中包含二维码,就会产生输出@property(nonatomic)AVCaptureMetadataOutput *output;//session:由他把输入输出结合在一起,并开始启动捕获设备(摄像头)@property(nonatomic)AVCaptureSession *session;//图像预览层,实时显示捕获的图像@property(nonatomic)AVCaptureVideoPreviewLayer *previewLayer;

初始化各对象,输入输出设备结合

- (void)creatCaptureDevice{//使用AVMediaTypeVideo 指明self.device代表视频,默认使用后置摄像头进行初始化self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];//使用设备初始化输入self.input = [[AVCaptureDeviceInput alloc]initWithDevice:self.device error:nil];//生成输出对象self.output = [[AVCaptureMetadataOutput alloc]init];//设置代理,一旦扫描到指定类型的数据,就会通过代理输出//在扫描的过程中,会分析扫描的内容,分析成功后就会调用代理方法在队列中输出[self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];//生成会话,用来结合输入输出self.session = [[AVCaptureSession alloc]init];if ([self.session canAddInput:self.input]) {[self.session addInput:self.input];}if ([self.session canAddOutput:self.output]) {[self.session addOutput:self.output];}//指定当扫描到二维码的时候,产生输出//AVMetadataObjectTypeQRCode 指定二维码//指定识别类型一定要放到添加到session之后[self.output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];//设置扫描信息的识别区域,左上角为(0,0),右下角为(1,1),不设的话全屏都可以识别。设置过之后可以缩小信息扫描面积加快识别速度。//这个属性并不好设置,整了半天也没太搞明白,到底x,y,width,height,怎么是对应的,这是我一点一点试的扫描区域,看不到只能调一下,扫一扫试试[self.output setRectOfInterest:CGRectMake(0.1 ,0.3 , 0.4, 0.4)];//使用self.session,初始化预览层,self.session负责驱动input进行信息的采集,layer负责把图像渲染显示self.previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.session];self.previewLayer.frame = CGRectMake(0, 0, kScreenWidth , kScreenHeight);self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;[self.view.layer addSublayer:self.previewLayer];//开始启动[self.session startRunning];}

实现代理方法

#pragma mark 输出的代理//metadataObjects :把识别到的内容放到该数组中- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{//停止扫描[self.session stopRunning];[self.timer invalidate];self.timer = nil;[self.lineView removeFromSuperview];if ([metadataObjects count] >= 1) {//数组中包含的都是AVMetadataMachineReadableCodeObject 类型的对象,该对象中包含解码后的数据AVMetadataMachineReadableCodeObject *qrObject = [metadataObjects lastObject];拿到扫描内容在这里进行个性化处理NSLog(@"识别成功%@",qrObject.stringValue);}}

遇到的问题和解决办法

(1)二维码上加logo图的时候,图片很模糊,这是由于UIGraphicsBeginImageContextWithOptions里的 scale 造成的,由于 iPhone 的屏幕都是retina屏幕,都是2倍,3倍像素,这里的 scale 要根据屏幕来设置 即[[UIScreen mainScreen] scale]这样图片就会很清晰

(2)setRectOfInterest:设置扫描信息的识别区域,左上角为(0,0),右下角为(1,1),不设的话全屏都可以识别。设置过之后可以缩小信息扫描面积加快识别速度,原来扫描的是整个屏幕的大小,这时候只扫描一块区域,以此加快识别速度。setRectOfInterest其实设置的是一个比例分别相对于屏幕的宽和高,所以CGRectMake的4个值范围必须在0-1,对应的xy width height正好相反, 即(y/SCREEN_HEIGHT, x/SCREEN_WIDTH, height/SCREEN_HEIGHT, width/SCREEN_WIDTH),还要注意的是原点不在左上角,而是在右上角。(也许这种表述不正确,但是按照这种方式去处理,就能准确的确定区域了)

举个例子:

[self.output setRectOfInterest:CGRectMake(95/SCREEN_HEIGHT, 40/SCREEN_WIDTH, 240/SCREEN_HEIGHT, 240/SCREEN_WIDTH];