二维码的扫描、识别与生成
详细易懂的二维码的扫描、识别与生成。
ios7之前我们实现二维码扫描是借助第三方(ZBar
,ZXing
等)来实现的,在ios7之后系统自己提供二维码扫描的方法,性能也要比第三方更好。
今天就来介绍一下原生二维码的使用,包括扫描二维码
,从图片扫描二维码
和生成二维码
。讲解中只展示部分代码,具体请看Github Demo,里面的代码不多,也很容易看懂。
扫描二维码
二维码扫描需要用到AVFoundation.framework
,需要用先创建一个AVCaptureSession,然后设置输入输出流,以及扫描区域和支持的格式:
//获取摄像设备
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
//创建输入流
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
if (!input)
{
return nil;
}
//创建输出流
AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
//设置代理 在主线程里刷新
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
//设置扫描区域的比例
CGFloat width = 300 / CGRectGetHeight(self.view.frame);
CGFloat height = 300 / CGRectGetWidth(self.view.frame);
output.rectOfInterest = CGRectMake((1 - width) / 2, (1- height) / 2, width, height);
AVCaptureSession *session = [[AVCaptureSession alloc] init];
//高质量采集率
[session setSessionPreset:AVCaptureSessionPresetHigh];
[session addInput:input];
[session addOutput:output];
//设置扫码支持的编码格式(这里设置条形码和二维码兼容)
output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode,
AVMetadataObjectTypeEAN13Code,
AVMetadataObjectTypeEAN8Code,
AVMetadataObjectTypeCode128Code];
然后用这个session
生成一个AVCaptureVideoPreviewLayer
加到某个view的layer上,就可以实时显示摄像头捕捉的内容了:
AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
layer.frame = self.view.layer.bounds;
[self.view.layer insertSublayer:layer atIndex:0];
然后调用[self.session startRunning];
开始捕获,当扫描出结果后会调用下面的代理方法,其中metadataObject.stringValue
就是扫描后的结果。
#pragma mark - AVCaptureMetadataOutputObjectsDelegate
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
if (metadataObjects.count > 0)
{
AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects firstObject];
}
}
为了在黑夜也可以很好的扫描,可以设置一个闪光灯的开关:
#pragma mark - 开关闪光灯
- (void)rightBarButtonDidClick:(UIBarButtonItem *)item
{
self.flashOpen = !self.flashOpen;
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if ([device hasTorch] && [device hasFlash])
{
[device lockForConfiguration:nil];
if (self.flashOpen)
{
device.torchMode = AVCaptureTorchModeOn;
device.flashMode = AVCaptureFlashModeOn;
}
else
{
device.torchMode = AVCaptureTorchModeOff;
device.flashMode = AVCaptureFlashModeOff;
}
[device unlockForConfiguration];
}
}
从图片扫描
有时候我们需要从图片中扫描二维码,或者从相册选择一张图片,代码如下,具体可以看demo。其中feature.messageString
就是扫描后的结果。
- (void)findQRCodeFromImage:(UIImage *)image
{
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode
context:nil
options:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}];
NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];
if (features.count >= 1)
{
CIQRCodeFeature *feature = [features firstObject];
}
}
生成二维码
生成二维码的代码很简单,代码如下。
/** 生成指定大小的黑白二维码 */
- (UIImage *)createQRImageWithString:(NSString *)string size:(CGSize)size
{
NSData *stringData = [string dataUsingEncoding:NSUTF8StringEncoding];
CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
// NSLog(@"%@",qrFilter.inputKeys);
[qrFilter setValue:stringData forKey:@"inputMessage"];
[qrFilter setValue:@"M" forKey:@"inputCorrectionLevel"];
CIImage *qrImage = qrFilter.outputImage;
//放大并绘制二维码 (上面生成的二维码很小,需要放大)
CGImageRef cgImage = [[CIContext contextWithOptions:nil] createCGImage:qrImage fromRect:qrImage.extent];
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
//翻转一下图片 不然生成的QRCode就是上下颠倒的
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGContextGetClipBoundingBox(context), cgImage);
UIImage *codeImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGImageRelease(cgImage);
return codeImage;
}
上面默认生成的时黑白二维码,不过我们也可以改颜色:
/** 为二维码改变颜色 */
- (UIImage *)changeColorForQRImage:(UIImage *)image backColor:(UIColor *)backColor frontColor:(UIColor *)frontColor
{
CIFilter *colorFilter = [CIFilter filterWithName:@"CIFalseColor"
keysAndValues:
@"inputImage",[CIImage imageWithCGImage:image.CGImage],
@"inputColor0",[CIColor colorWithCGColor:frontColor.CGColor],
@"inputColor1",[CIColor colorWithCGColor:backColor.CGColor],
nil];
return [UIImage imageWithCIImage:colorFilter.outputImage];
}
有的二维码也会在中心加一个小图片,例如用户头像,代码如下:
/** 在二维码中心加一个小图 */
- (UIImage *)addSmallImageForQRImage:(UIImage *)qrImage
{
UIGraphicsBeginImageContext(qrImage.size);
[qrImage drawInRect:CGRectMake(0, 0, qrImage.size.width, qrImage.size.height)];
UIImage *image = [UIImage imageNamed:@"small"];
CGFloat imageW = 50;
CGFloat imageX = (qrImage.size.width - imageW) * 0.5;
CGFloat imgaeY = (qrImage.size.height - imageW) * 0.5;
[image drawInRect:CGRectMake(imageX, imgaeY, imageW, imageW)];
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
其实也可以扫描条形码,大家可以对着条形码试一试,代码都是通用的。