美文网首页iOS开发进阶
轻量级扫一扫,二维码完整demo

轻量级扫一扫,二维码完整demo

作者: 达摩君 | 来源:发表于2017-12-06 16:44 被阅读66次

    由于项目中需要有扫一扫功能,那是开发周期比较紧张,就直接GitHub找了ZXingObjC直接使用。但是发现这个第三方功能太齐全,对于我显得太笨重。根本不需要那么多功能。所以就自己东抄抄西抄抄整理了一份轻量级的demo。
    话不多说,先放demo地址,有需要的可以下载使用。
    现在分析项目整体功能:

    项目目录.png
    首先是是判断相机权限,假如未决定就请求弹框:
    + (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);
    }
    
    ok~ 基本核心功能代码都结束。具体可以下载demo看看哦~ 源文件太大免费压缩还有水印,哎~.gif

    相关文章

      网友评论

        本文标题:轻量级扫一扫,二维码完整demo

        本文链接:https://www.haomeiwen.com/subject/kohlixtx.html