IOS二维码(ZXingObjc)
IOS二维码(ZXingObjc)
前言
公司最近上线一款产品,是通过二维码扫描登录。开发阶段的时候使用的是IOS原生的AVFoundation框架。但是生产线做出的二维码却意外的扫不出来。最终是换用ZXingObjC才解决问题。
原生的能扫出来么?ZXingObjC
ZXing是一个开放源码的,用Java实现的多种格式的1D/2D条码图像处理库,目前由Google维护更新。ZXingObjC是ZXing的objective-c版本库,在许多IOS开发项目中使用。
下载安装
依赖库和编译
ZXingObjC在IOS中主要依赖以下框架:
- AVFoundation.framework
- CoreGraphics.framework
- CoreMedia.framework
- CoreVideo.framework
- Foundation.framework
- ImageIO.framework
- QuartzCore.framework
- UIKit.framework
将上述框架添加到项目中就能正常编译。
设置相册访问描述
添加相册访问
ZXCaptureDelegate
ZXing的使用很简单,生成ZXCapture
捕获器,然后将ZXCapture
的layer.frame设成控制器view的大小,然后将ZXCapture
的layer添加到view的layer上,最后设置delegate就完成了配置。当摄像头扫描到二维码或者条形码的时候,就会调用- (void)captureResult:(ZXCapture *)capture result:(ZXResult *)result
方法,参考代码如下:
- (void)setupCapture{
self.capture = [[ZXCapture alloc]init];
self.capture.camera = self.capture.back;
//自动聚焦
self.capture.focusMode = AVCaptureFocusModeContinuousAutoFocus;
self.capture.layer.frame = CGRectMake(0, 64, SCREEN_WIDTH, SCREEN_HEIGHT - 64);
[self.view.layer addSublayer:self.capture.layer];
[self.view bringSubviewToFront:self.scanSuperView];
for (UIView * view in _blackBackgroundViewsArray) {
[self.view bringSubviewToFront:view];
}
self.capture.delegate = self;
//设置扫描区域
xxxxx
}
#pragma mark - ZXCaptureDelegate
- (void)captureResult:(ZXCapture *)capture result:(ZXResult *)result {
if (!result) return;
NSLog(@"\n%@\n",result.text);
if (result.barcodeFormat == kBarcodeFormatQRCode) {
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
[self.capture stop];
[[[UIAlertView alloc]initWithTitle:@"结果" message: result.text delegate:self cancelButtonTitle:@"好的" otherButtonTitles: nil] show];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self.capture start];
});
}
}
关于旋转
捕获器默认的获取精度是1280*720,但是它是横屏的,如果要竖屏扫描就必须旋转下。下面是旋转代码。
//旋转
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
float scanRectRotation;
float captureRotation;
switch (orientation) {
case UIInterfaceOrientationPortrait:
captureRotation = 0;
scanRectRotation = 90;
break;
case UIInterfaceOrientationLandscapeLeft:
captureRotation = 90;
scanRectRotation = 180;
break;
case UIInterfaceOrientationLandscapeRight:
captureRotation = 270;
scanRectRotation = 0;
break;
case UIInterfaceOrientationPortraitUpsideDown:
captureRotation = 180;
scanRectRotation = 270;
break;
default:
captureRotation = 0;
scanRectRotation = 90;
break;
}
CGAffineTransform transform = CGAffineTransformMakeRotation((CGFloat) (captureRotation / 180 * M_PI));
[self.capture setTransform:transform];
[self.capture setRotation:scanRectRotation];
关于扫描区域(scanRect)的计算(重点)
ZXing的scanRect的设置网上有很多错误说法,有的直接贴上复杂的代码,然而没有一个很好的解释scanRect的数据是怎么来的。下面我就举一个例子,假设capture.sessionPreset
设置的是AVCaptureSessionPreset1280x720
(如果没设默认是1280*720),要将其投射到一个iPad上,如图蓝色所示,中间绿色的部分是我们想要的扫描区域。这时候,capture.scanRect
并不是scanView1.frame
而是 scanView2.frame
。因此我们要做一道数学题,将scanView2.frame
通过scanView1.frame
等比例缩放计算出来。
首先是计算缩放比例:
图中例子中,长方形缩放到正方形屏幕,肯定有一部分多出来,因此长宽比不可能相同,到底是用scanView1.width / scanView2.width 还是 scanView1.height / scanView2.height 作为缩放比例呢?通过简单计算得出结论(可以自己举例子试下):
- scale = max(scanView1.width / scanView2.width , scanView1.height / scanView2.height)
比例确定下来了,长和宽就很好计算了
-
height = scanView1.height / scale
-
width = scanView1.width / scale
到这里需要注意下x和y的计算,如果多出来的是height,那么x就能按比例计算,y通过x与y的比例计算。如果多出来的是width,那么y久能按比例算出来,x通过x与y的比例计算。
if (scanView1.width / scanView2.width > scanView1.height / scanView2.height){
x = scanView1.x / scale;
y = x * scanView1.y / scanView1.x;
}
else {
y = scanView1.y / scale;
x = y * scanView1.x / scanView1.y;
}
最后就可以得到scanRect
- scanRect = CGRectmake(x,y,width,height)
demo下载
- 百度云盘:下载地址