由于项目中需要有扫一扫功能,那是开发周期比较紧张,就直接GitHub找了ZXingObjC
直接使用。但是发现这个第三方功能太齐全,对于我显得太笨重。根本不需要那么多功能。所以就自己东抄抄西抄抄整理了一份轻量级的demo。
话不多说,先放demo地址,有需要的可以下载使用。
现在分析项目整体功能:
首先是是判断相机权限,假如未决定就请求弹框:
+ (void)requestCameraPemissionWithResult:(void (^)(BOOL))completion {
AVAuthorizationStatus permission = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
switch (permission) {
case AVAuthorizationStatusAuthorized:
completion(YES);
break;
case AVAuthorizationStatusDenied:
case AVAuthorizationStatusRestricted:
completion(NO);
case AVAuthorizationStatusNotDetermined:
{
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
dispatch_async(dispatch_get_main_queue(), ^{
if (granted) {
completion(YES);
} else {
completion(NO);
}
});
}];
}
default:
break;
}
}
判断可以使用照相机后设置相机:
- (void)initParaWithPreView:(UIView*)videoPreView ObjectType:(NSArray*)objType cropRect:(CGRect)cropRect success:(void(^)(NSArray<NSString*> *array))block {
self.blockScanResult = block;
self.videoPreView = videoPreView;
_session = [[AVCaptureSession alloc] init];
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error;
_deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (_deviceInput) {
[_session addInput:_deviceInput];
AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init];
[metadataOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
[_session addOutput:metadataOutput];
metadataOutput.metadataObjectTypes = objType;
_stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG, AVVideoCodecKey, nil];
[_stillImageOutput setOutputSettings:outputSettings];
[_session addOutput:_stillImageOutput];
AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:_session];
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
previewLayer.frame = cropRect;
[videoPreView.layer insertSublayer:previewLayer atIndex:0];
//先进行判断是否支持控制对焦,不开启自动对焦功能,很难识别二维码。
if (_deviceInput.device.isFocusPointOfInterestSupported && [_deviceInput.device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
[_deviceInput.device lockForConfiguration:nil];
[_deviceInput.device setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
[_deviceInput.device unlockForConfiguration];
}
} else {
[LEEAlertViewController showWithTitle:@"错误" message:[NSString stringWithFormat:@"%@",error]];
}
}
扫描到结果的代理回调:
- (void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
AVMetadataMachineReadableCodeObject *metadataObject = metadataObjects.firstObject;
if ([metadataObject.type isEqualToString:AVMetadataObjectTypeQRCode]) {
[self stopScan];
self.blockScanResult(@[metadataObject.stringValue]);
}
}
开始扫码:
- (void)startScan {
if (_session != nil && !_session.isRunning) {
[_session startRunning];
}
}
结束扫码
- (void)stopScan {
if (_session != nil && _session.isRunning) {
[_session stopRunning];
}
}
好的,现在已经能完成基本的扫描功能了,接下来要开始加点视图和动画了:
- (void)drawScanRect {
CGSize sizeRetangle = CGSizeMake(self.frame.size.width - self.viewStyle.xScanRetangleOffset * 2, self.frame.size.width - self.viewStyle.xScanRetangleOffset * 2);
//扫码区域Y轴最小坐标
CGFloat YMinRetangle = self.frame.size.height / 2.0 - sizeRetangle.height / 2.0 - self.viewStyle.centerUpOffset;
CGFloat YMaxRetangle = YMinRetangle + sizeRetangle.height;
CGFloat XRetangleRight = self.frame.size.width - self.viewStyle.xScanRetangleOffset;
CGContextRef context = UIGraphicsGetCurrentContext();
{//非扫码区域半透明
const CGFloat *components = CGColorGetComponents(self.viewStyle.notRecoginitonArea.CGColor);
CGFloat red = components[0];
CGFloat green = components[1];
CGFloat bule = components[2];
CGFloat alpha = components[3];
CGContextSetRGBFillColor(context, red, green, bule, alpha);
//填充矩形
//上
CGRect rect = CGRectMake(0, 0, self.frame.size.width, YMinRetangle);
CGContextFillRect(context, rect);
//左 加了一点点,不然有缝隙,why?
rect = CGRectMake(0, YMinRetangle - 0.05, self.viewStyle.xScanRetangleOffset, sizeRetangle.height + 0.1);
CGContextFillRect(context, rect);
//右
rect = CGRectMake(XRetangleRight, YMinRetangle - 0.05, self.viewStyle.xScanRetangleOffset, sizeRetangle.height + 0.1);
CGContextFillRect(context, rect);
//下
rect = CGRectMake(0, YMaxRetangle, self.frame.size.width, self.frame.size.height - YMaxRetangle);
CGContextFillRect(context, rect);
CGContextStrokePath(context);
}
if (self.viewStyle.isNeedShowRetangle) {//画矩形框
CGContextSetStrokeColorWithColor(context, self.viewStyle.colorRetangleLine.CGColor);
CGContextSetLineWidth(context, 1);
CGContextAddRect(context, CGRectMake(self.viewStyle.xScanRetangleOffset, YMinRetangle, sizeRetangle.width, sizeRetangle.height));
CGContextStrokePath(context);
}
_scanRetangleRect = CGRectMake(self.viewStyle.xScanRetangleOffset, YMinRetangle, sizeRetangle.width, sizeRetangle.height);
//画矩形框4格外围相框角
//相框角的宽度和高度
int wAngle = self.viewStyle.photoframeAngleW;
int hAngle = self.viewStyle.photoframeAngleH;
//4个角的 线的宽度
CGFloat linewidthAngle = self.viewStyle.photoframeLineW;// 经验参数:6和4
//画扫码矩形以及周边半透明黑色坐标参数
CGFloat diffAngle = 0.0f;
switch (_viewStyle.photoFrameAngleStyle)
{
case LEEScanViewPhotoFrameAngleStyle_Outer:
{
diffAngle = linewidthAngle/3;//框外面4个角,与框紧密联系在一起
}
break;
case LEEScanViewPhotoFrameAngleStyle_On:
{
diffAngle = 0;
}
break;
case LEEScanViewPhotoFrameAngleStyle_Inner:
{
diffAngle = -linewidthAngle/2;
}
break;
default:
{
diffAngle = linewidthAngle/3;
}
break;
}
CGContextSetStrokeColorWithColor(context, _viewStyle.colorAngle.CGColor);
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
// Draw them with a 2.0 stroke width so they are a bit more visible.
CGContextSetLineWidth(context, linewidthAngle);
//
CGFloat leftX = self.viewStyle.xScanRetangleOffset - diffAngle;
CGFloat topY = YMinRetangle - diffAngle;
CGFloat rightX = XRetangleRight + diffAngle;
CGFloat bottomY = YMaxRetangle + diffAngle;
//左上角水平线
CGContextMoveToPoint(context, leftX-linewidthAngle/2, topY);
CGContextAddLineToPoint(context, leftX + wAngle, topY);
//左上角垂直线
CGContextMoveToPoint(context, leftX, topY-linewidthAngle/2);
CGContextAddLineToPoint(context, leftX, topY+hAngle);
//左下角水平线
CGContextMoveToPoint(context, leftX-linewidthAngle/2, bottomY);
CGContextAddLineToPoint(context, leftX + wAngle, bottomY);
//左下角垂直线
CGContextMoveToPoint(context, leftX, bottomY+linewidthAngle/2);
CGContextAddLineToPoint(context, leftX, bottomY - hAngle);
//右上角水平线
CGContextMoveToPoint(context, rightX+linewidthAngle/2, topY);
CGContextAddLineToPoint(context, rightX - wAngle, topY);
//右上角垂直线
CGContextMoveToPoint(context, rightX, topY-linewidthAngle/2);
CGContextAddLineToPoint(context, rightX, topY + hAngle);
//右下角水平线
CGContextMoveToPoint(context, rightX+linewidthAngle/2, bottomY);
CGContextAddLineToPoint(context, rightX - wAngle, bottomY);
//右下角垂直线
CGContextMoveToPoint(context, rightX, bottomY+linewidthAngle/2);
CGContextAddLineToPoint(context, rightX, bottomY - hAngle);
CGContextStrokePath(context);
}
扫描框有了,开始设置动画:
- (void)startScanAnimation {
switch (self.viewStyle.animationStyle) {
case LEEScanViewAnimationStyle_LineMove:
{
//线动画
if (!_scanLineAnimation){
self.scanLineAnimation = [[LEEScanLineAnimation alloc]init];
}
[_scanLineAnimation startAnimatingWithRect:_scanRetangleRect
InView:self
Image:_viewStyle.animationImage];
}
break;
case LEEScanViewAnimationStyle_NetGrid:
{
//网格动画
if (!_scanNetAnimation)
self.scanNetAnimation = [[LEEScanNetAnimation alloc]init];
[_scanNetAnimation startAnimatingWithRect:_scanRetangleRect
InView:self
Image:_viewStyle.animationImage];
}
break;
case LEEScanViewAnimationStyle_LineStill:
{
if (!_scanLineStill) {
CGRect stillRect = CGRectMake(_scanRetangleRect.origin.x+20,
_scanRetangleRect.origin.y + _scanRetangleRect.size.height/2,
_scanRetangleRect.size.width-40,
2);
_scanLineStill = [[UIImageView alloc]initWithFrame:stillRect];
_scanLineStill.image = _viewStyle.animationImage;
}
[self addSubview:_scanLineStill];
}
break;
default:
break;
}
}
停止动画:
- (void)stopScanAnimation {
if (_scanLineAnimation) {
[_scanLineAnimation stopAnimating];
}
if (_scanNetAnimation) {
[_scanNetAnimation stopAnimating];
}
if (_scanLineStill) {
[_scanLineStill removeFromSuperview];
}
}
好了,现在有框框,有动画了,再来个散光灯:
//开关闪光灯
- (void)changeTorch {
AVCaptureTorchMode torch = _deviceInput.device.torchMode;
switch (torch) {
case AVCaptureTorchModeAuto:
break;
case AVCaptureTorchModeOff:
torch = AVCaptureTorchModeOn;
break;
case AVCaptureTorchModeOn:
torch = AVCaptureTorchModeOff;
break;
default:
break;
}
[_deviceInput.device lockForConfiguration:nil];
_deviceInput.device.torchMode = torch;
[_deviceInput.device unlockForConfiguration];
}
闪光灯有了,再来个手势放大镜头:
//设置镜头缩放
- (void)setVideoScale:(CGFloat)scale {
//一开始系统默认是1
[_deviceInput.device lockForConfiguration:nil];
AVCaptureConnection *videoConnection = [self connectionWithMediaType:AVMediaTypeVideo fromConnections:self.stillImageOutput.connections];
CGFloat zoom = scale / videoConnection.videoScaleAndCropFactor;
//videoScaleAndCropFactor cannot be set to a value less than 1.0
if (scale >= 1) {
videoConnection.videoScaleAndCropFactor = scale;
}
[_deviceInput.device unlockForConfiguration];
if (scale >= 1) {
CGAffineTransform transform = self.videoPreView.transform;
self.videoPreView.transform = CGAffineTransformScale(transform, zoom, zoom);
}
}
好滴,现在再来个相册中选取照片识别功能:
#pragma mark : - 打开相册
- (void)openPhotoesLib {
if ([LEEScanPhotoPermissions photoPermission]) {
[self openLocalPhoto];
} else {
[LEEAlertViewController showWithTitle:@"提示" message:@"请到设置->隐私中开启本程序相册权限"];
}
}
- (void)openLocalPhoto {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
picker.delegate = self;
[self presentViewController:picker animated:YES completion:nil];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
[picker dismissViewControllerAnimated:YES completion:nil];
UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];
if (image == nil) {
image = [info objectForKey:UIImagePickerControllerOriginalImage];
}
[LEEScanNative recognizeImage:image success:^(NSArray *result) {
for (int i = 0; i < result.count; i ++) {
[self getResultAndDoSomething:result[I]];
}
if (result.count == 0) {
[LEEAlertViewController showWithTitle:@"提示" message:@"未检测到有二维码哦"];
}
}];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[picker dismissViewControllerAnimated:YES completion:nil];
}
+ (void)recognizeImage:(UIImage *)image success:(void(^)(NSArray *))block {
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}];
NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];
NSMutableArray *mutableStr = [NSMutableArray array];
for (int i = 0; i < features.count; i ++) {
CIQRCodeFeature *feature = features[I];
NSString *scannedResult = feature.messageString;
[mutableStr addObject:scannedResult];
}
block(mutableStr.copy);
}
网友评论