博客> iOS - 二维码扫描和生成相关 (待更新)
iOS - 二维码扫描和生成相关 (待更新)
2017-12-13 09:40 评论:0 阅读:320 OS_JJ
ios - 二维码扫描和生成相关 (待更新)
今天看到一篇新的博客,介绍了系统自带的方法创建二维码扫描功能,所以立贴准备把自己二维码扫描相关的代码好步骤写出来 ! 待更新 !

传送地址

 7A950534-40DF-4627-9C0A-CFE7A830D6FC.png

一、原生二维码实现

**1)控制器代码相关**

    1.导入 Framework  :  #import <AVFoundation>

    2.实现代理协议 :         
        AVCaptureMetadataOutputObjectsDelegate
        UINavigationControllerDelegate
        UIImagePickerControllerDelegate

    3.属性相关
        /// 二维码扫描相关
        @property (strong,nonatomic)AVCaptureSession * session;
        @property (strong,nonatomic)AVCaptureVideoPreviewLayer * previewLayer;

        /*** 专门用于保存描边的图层 ***/
        @property (nonatomic,strong) MSUScanView *scanView;

    4.控制器里 : ViewDidLoad 里面实现相关方法
            // 导航栏
            [self createNavView];
            // 中部视图
            [self.view addSubview:self.scanningView];
            [self setupMSUCodeScanning];

    5.导航栏   (略) 

    6.二维码相关 

            - (void)setupMSUCodeScanning {
                // (1)、获取摄像设备
                AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

                // (2)、创建输入流
                AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];

                // (3)、创建输出流
                AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];

                // (4)、设置代理 在主线程里刷新
                [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];

                // 设置扫描范围(每一个取值0~1,以屏幕右上角为坐标原点)
                // 注:微信二维码的扫描范围是整个屏幕,这里并没有做处理(可不用设置)
                output.rectOfInterest = CGRectMake(-0.2, 0.2, 0.7, 0.6);
            //    output.rectOfInterest = CGRectMake(0.05, 0.2, 0.7, 0.6);

                // (5)、初始化链接对象(会话对象)
                self.session = [[AVCaptureSession alloc] init];
                // 高质量采集率
                [_session setSessionPreset:AVCaptureSessionPresetHigh];

                // (5.1) 添加会话输入
                [_session addInput:input];

                // (5.2) 添加会话输出
                [_session addOutput:output];

                // (6)、设置输出数据类型,需要将元数据输出添加到会话后,才能指定元数据类型,否则会报错
                // 设置扫码支持的编码格式(如下设置条形码和二维码兼容)
                output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code,  AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code];

                // (7)、实例化预览图层, 传递_session是为了告诉图层将来显示什么内容
                self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_session];
                _previewLayer.frame = CGRectMake(0, 64, WIDTH, HEIGHT-64);
                _previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;

                //( 8)、将图层插入当前视图
                [self.view.layer insertSublayer:_previewLayer atIndex:0];

                // (9)、启动会话
                [_session startRunning];
            }

    7.生命周期中相关操作及scanningView的初始化

        - (void)viewDidAppear:(BOOL)animated {
            [super viewDidAppear:animated];
            [self.scanView addTimer];
        }
        - (void)viewWillDisappear:(BOOL)animated {
            [super viewWillDisappear:animated];
            [self.scanView removeTimer];
        }

        - (void)dealloc {
            NSLog(@"dealloc");
            [self removeScanningView];
        }

        - (MSUScanView *)scanningView {
            if (!_scanView) {
                _scanView = [MSUScanView scanningViewWithFrame:CGRectMake(0, 64, WIDTH, HEIGHT-64) layer:self.view.layer];
            }
            return _scanView;
        }

        //移除扫描视图
        - (void)removeScanningView {
            [self.scanningView removeTimer];
            [self.scanningView removeFromSuperview];
            self.scanView = nil;
        }

    8.相册按钮相关 (MSUPermissionTool 为封装的权限工具类)

        // 相册按钮点击 
        - (void)photoBtnClick:(UIButton *)sender{
            // 1、 获取摄像设备
            AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
            if (device) {
                [MSUPermissionTool getPhotosPermission:^(NSInteger authStatus) {
                    if (authStatus == 1) {
                        dispatch_async(dispatch_get_main_queue(), ^{
                            UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
                            imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; //(选择类型)表示仅仅从相册中选取照片
                            imagePicker.delegate = self;
                            [self presentViewController:imagePicker animated:YES completion:^{
                                [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
                            }];
                        });

                    }else{
                        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"温馨提示" message:@"请去-> [设置 - 隐私 - 照片 - 秀贝] 打开访问开关" preferredStyle:UIAlertControllerStyleAlert];
                        [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
                            //确定按钮点击事件处理
                        }]];
                        [self presentViewController:alert animated:YES completion:nil];
                    }
                }];
            }
        }

    9.代理相关

        #pragma mark - 代理事件
         #pragma mark -- 二维码代理(AVCaptureMetadataOutputObjectsDelegate)
        - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
        {
            // 扫描成功之后的提示音
           [self MN_playSoundEffect:@"sound.caf"];

            NSString *stringValue;
            if ([metadataObjects count] >0){
                //停止扫描
                [_session stopRunning];
                AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjects objectAtIndex:0];
                stringValue = metadataObject.stringValue;

                //代码封装抽离用 , 将扫描方法和扫描结果两块处理逻辑分开 此处为发送扫描结果
                // [[NSNotificationCenter defaultCenter] postNotificationName:@"MSUCodeResultFromeScanning" object:metadataObject.stringValue];

                if ([stringValue hasPrefix:@"http"]) {// 扫描结果为二维码
                    NSLog(@"二维码%@",stringValue);

                } else { // 扫描结果为条形码
                    NSLog(@"条形码%@",stringValue);

                }
            }
        }

    /** 播放音效文件 */
    - (void)MN_playSoundEffect:(NSString *)name {
        // 获取音效
        NSString *audioFile = [[NSBundle mainBundle] pathForResource:name ofType:nil];
        NSURL *fileUrl = [NSURL fileURLWithPath:audioFile];

        // 1、获得系统声音ID
        SystemSoundID soundID = 0;

        AudioServicesCreateSystemSoundID((__bridge CFURLRef)(fileUrl), &soundID);

        AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, soundCompleteCallback, NULL);

        // 2、播放音频
        AudioServicesPlaySystemSound(soundID); // 播放音效
    }
    /** 播放完成回调函数 */
    void soundCompleteCallback(SystemSoundID soundID, void *clientData){
        //NSLog(@"播放完成...");
    }

     #pragma mark -- 相册代理(UIImagePickerControllerDelegate)
    /** 相册选择 */
    - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString> *)info{
        [self.view addSubview:self.scanningView];
        [self dismissViewControllerAnimated:YES completion:^{
            [self scanMSUResultromPhotosInTheAlbum:[info objectForKey:@"UIImagePickerControllerOriginalImage"]];
        }];
    }

    /** 相册取消 */
    - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker{
        [self.view addSubview:self.scanningView];
        [self dismissViewControllerAnimated:YES completion:nil];
        [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
    }

    /** 播放完成回调函数 */
    - (void)scanMSUResultromPhotosInTheAlbum:(UIImage *)image{
        // 对选取照片的处理,如果选取的图片尺寸过大,则压缩选取图片,否则不作处理
        image = [UIImage imageSizeWithScreenImage:image];

        // CIDetector(CIDetector可用于人脸识别)进行图片解析,从而使我们可以便捷的从相册中获取到二维码
        // 声明一个CIDetector,并设定识别类型 CIDetectorTypeQRCode
        CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{ CIDetectorAccuracy : CIDetectorAccuracyHigh }];

        // 取得识别结果
        NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];
        for (int index = 0; index < [features count]; index ++) {
            CIQRCodeFeature *feature = [features objectAtIndex:index];
            NSString *stringValue = feature.messageString;
            NSLog(@"scannedResult - - %@", stringValue);

            //代码封装抽离用 , 将扫描方法和扫描结果两块处理逻辑分开 此处为发送扫描结果
            // [[NSNotificationCenter defaultCenter] postNotificationName:@"MSUCodeResultFromeAibum" object:scannedResult];

            // 处理相关逻辑地方

        }

    }

**2)View代码相关**

     **.h文件相关diamante**
        /**
         *  对象方法创建SGQRCodeScanningView
         *
         *  @param frame     frame
         *  @param layer     父视图 layer
         */
        - (instancetype)initWithFrame:(CGRect)frame layer:(CALayer *)layer;
        /**
         *  类方法创建SGQRCodeScanningView
         *
         *  @param frame     frame
         *  @param layer     父视图 layer
         */
        + (instancetype)scanningViewWithFrame:(CGRect )frame layer:(CALayer *)layer;

        /** 添加定时器 */
        - (void)addTimer;
        /** 移除定时器(切记:一定要在Controller视图消失的时候,停止定时器) */
        - (void)removeTimer;

    **.m文件代码相关**
    1.宏定义及属性相关
            /** 扫描内容的Y值 */
            #define scanContent_Y (self.frame.size.height) * 0.24
            /** 扫描内容的X值 */
            #define scanContent_X self.frame.size.width * 0.15

            #define SGQRCodeScanningLineAnimation 0.05
            #define NavHeight 64

            @property (nonatomic, strong) AVCaptureDevice *device;
            @property (nonatomic, strong) CALayer *tempLayer;
            @property (nonatomic, strong) UIImageView *scanningline;
            @property (nonatomic, strong) NSTimer *timer;

            /** 扫描动画线(冲击波) 的高度 */
            static CGFloat const scanninglineHeight = 12;
            /** 扫描内容外部View的alpha值 */
            static CGFloat const scanBorderOutsideViewAlpha = 0.4;

    2.视图代码相关

        - (CALayer *)tempLayer {
            if (!_tempLayer) {
                _tempLayer = [[CALayer alloc] init];
            }
            return _tempLayer;
        }

        - (instancetype)initWithFrame:(CGRect)frame layer:(CALayer *)layer {
            if (self = [super initWithFrame:frame]) {
                self.tempLayer = layer;

                // 布局扫描界面
                [self setupSubviews];

            }
            return self;
        }

        + (instancetype)scanningViewWithFrame:(CGRect )frame layer:(CALayer *)layer {
            return [[self alloc] initWithFrame:frame layer:layer];
        }

        - (void)setupSubviews {
            // 扫描内容的创建
            CALayer *scanContent_layer = [[CALayer alloc] init];
            CGFloat scanContent_layerX = scanContent_X;
            CGFloat scanContent_layerY = scanContent_Y;
            CGFloat scanContent_layerW = self.frame.size.width - 2 * scanContent_X;
            CGFloat scanContent_layerH = scanContent_layerW;
            scanContent_layer.frame = CGRectMake(scanContent_layerX, scanContent_layerY, scanContent_layerW, scanContent_layerH);
            scanContent_layer.borderColor = [[UIColor whiteColor] colorWithAlphaComponent:0.6].CGColor;
            scanContent_layer.borderWidth = 0.7;
            scanContent_layer.backgroundColor = [UIColor clearColor].CGColor;
            [self.tempLayer addSublayer:scanContent_layer];

        #pragma mark - - - 扫描外部View的创建
            // 顶部layer的创建
            CALayer *top_layer = [[CALayer alloc] init];
            CGFloat top_layerX = 0;
            CGFloat top_layerY = 0;
            CGFloat top_layerW = self.frame.size.width;
            CGFloat top_layerH = scanContent_layerY - NavHeight;
            top_layer.frame = CGRectMake(top_layerX, top_layerY, top_layerW, top_layerH);
            top_layer.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:scanBorderOutsideViewAlpha].CGColor;
            [self.layer addSublayer:top_layer];

            // 左侧layer的创建
            CALayer *left_layer = [[CALayer alloc] init];
            CGFloat left_layerX = 0;
            CGFloat left_layerY = scanContent_layerY - NavHeight;
            CGFloat left_layerW = scanContent_X;
            CGFloat left_layerH = scanContent_layerH;
            left_layer.frame = CGRectMake(left_layerX, left_layerY, left_layerW, left_layerH);
            left_layer.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:scanBorderOutsideViewAlpha].CGColor;
            [self.layer addSublayer:left_layer];

            // 右侧layer的创建
            CALayer *right_layer = [[CALayer alloc] init];
            CGFloat right_layerX = CGRectGetMaxX(scanContent_layer.frame);
            CGFloat right_layerY = scanContent_layerY - NavHeight;
            CGFloat right_layerW = scanContent_X;
            CGFloat right_layerH = scanContent_layerH;
            right_layer.frame = CGRectMake(right_layerX, right_layerY, right_layerW, right_layerH);
            right_layer.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:scanBorderOutsideViewAlpha].CGColor;
            [self.layer addSublayer:right_layer];

            // 下面layer的创建
            CALayer *bottom_layer = [[CALayer alloc] init];
            CGFloat bottom_layerX = 0;
            CGFloat bottom_layerY = CGRectGetMaxY(scanContent_layer.frame) - NavHeight;
            CGFloat bottom_layerW = self.frame.size.width;
            CGFloat bottom_layerH = self.frame.size.height - bottom_layerY;
            bottom_layer.frame = CGRectMake(bottom_layerX, bottom_layerY, bottom_layerW, bottom_layerH);
            bottom_layer.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:scanBorderOutsideViewAlpha].CGColor;
            [self.layer addSublayer:bottom_layer];

            // 提示Label
            UILabel *promptLabel = [[UILabel alloc] init];
            promptLabel.backgroundColor = [UIColor clearColor];
            CGFloat promptLabelX = 0;
            CGFloat promptLabelY = CGRectGetMaxY(scanContent_layer.frame) + 30 - NavHeight;
            CGFloat promptLabelW = self.frame.size.width;
            CGFloat promptLabelH = 25;
            promptLabel.frame = CGRectMake(promptLabelX, promptLabelY, promptLabelW, promptLabelH);
            promptLabel.textAlignment = NSTextAlignmentCenter;
            promptLabel.font = [UIFont boldSystemFontOfSize:13.0];
            promptLabel.textColor = [[UIColor whiteColor] colorWithAlphaComponent:0.8];
            promptLabel.text = @"将二维码/条码放入框内, 即可自动扫描";
            [self addSubview:promptLabel];

            // 添加闪光灯按钮
            UIButton *light_button = [[UIButton alloc] init];
            CGFloat light_buttonX = 0;
            CGFloat light_buttonY = CGRectGetMaxY(promptLabel.frame) + scanContent_X * 0.5;
            CGFloat light_buttonW = self.frame.size.width;
            CGFloat light_buttonH = 25;
            light_button.frame = CGRectMake(light_buttonX, light_buttonY, light_buttonW, light_buttonH);
            [light_button setTitle:@"打开照明灯" forState:UIControlStateNormal];
            [light_button setTitle:@"关闭照明灯" forState:UIControlStateSelected];
            [light_button setTitleColor:promptLabel.textColor forState:(UIControlStateNormal)];
            light_button.titleLabel.font = [UIFont systemFontOfSize:17];

            [light_button addTarget:self action:@selector(light_buttonAction:) forControlEvents:UIControlEventTouchUpInside];
            [self addSubview:light_button];

        #pragma mark - - - 扫描边角imageView的创建
            // 左上侧的image
            CGFloat margin = 7;

            UIImage *left_image = [UIImage imageNamed:@"QRCodeLeftTop"];
            UIImageView *left_imageView = [[UIImageView alloc] init];
            CGFloat left_imageViewX = CGRectGetMinX(scanContent_layer.frame) - left_image.size.width * 0.5 + margin;
            CGFloat left_imageViewY = CGRectGetMinY(scanContent_layer.frame) - left_image.size.width * 0.5 + margin;
            CGFloat left_imageViewW = left_image.size.width;
            CGFloat left_imageViewH = left_image.size.height;
            left_imageView.frame = CGRectMake(left_imageViewX, left_imageViewY, left_imageViewW, left_imageViewH);
            left_imageView.image = left_image;
            [self.tempLayer addSublayer:left_imageView.layer];

            // 右上侧的image
            UIImage *right_image = [UIImage imageNamed:@"QRCodeRightTop"];
            UIImageView *right_imageView = [[UIImageView alloc] init];
            CGFloat right_imageViewX = CGRectGetMaxX(scanContent_layer.frame) - right_image.size.width * 0.5 - margin;
            CGFloat right_imageViewY = left_imageView.frame.origin.y;
            CGFloat right_imageViewW = left_image.size.width;
            CGFloat right_imageViewH = left_image.size.height;
            right_imageView.frame = CGRectMake(right_imageViewX, right_imageViewY, right_imageViewW, right_imageViewH);
            right_imageView.image = right_image;
            [self.tempLayer addSublayer:right_imageView.layer];

            // 左下侧的image
            UIImage *left_image_down = [UIImage imageNamed:@"QRCodeLeftBottom"];
            UIImageView *left_imageView_down = [[UIImageView alloc] init];
            CGFloat left_imageView_downX = left_imageView.frame.origin.x;
            CGFloat left_imageView_downY = CGRectGetMaxY(scanContent_layer.frame) - left_image_down.size.width * 0.5 - margin;
            CGFloat left_imageView_downW = left_image.size.width;
            CGFloat left_imageView_downH = left_image.size.height;
            left_imageView_down.frame = CGRectMake(left_imageView_downX, left_imageView_downY, left_imageView_downW, left_imageView_downH);
            left_imageView_down.image = left_image_down;
            [self.tempLayer addSublayer:left_imageView_down.layer];

            // 右下侧的image
            UIImage *right_image_down = [UIImage imageNamed:@"QRCodeRightBottom"];
            UIImageView *right_imageView_down = [[UIImageView alloc] init];
            CGFloat right_imageView_downX = right_imageView.frame.origin.x;
            CGFloat right_imageView_downY = left_imageView_down.frame.origin.y;
            CGFloat right_imageView_downW = left_image.size.width;
            CGFloat right_imageView_downH = left_image.size.height;
            right_imageView_down.frame = CGRectMake(right_imageView_downX, right_imageView_downY, right_imageView_downW, right_imageView_downH);
            right_imageView_down.image = right_image_down;
            [self.tempLayer addSublayer:right_imageView_down.layer];
        }

        #pragma mark - - - 照明灯的点击事件
        - (void)light_buttonAction:(UIButton *)button {
            if (button.selected == NO) { // 点击打开照明灯
                [self turnOnLight:YES];
                button.selected = YES;
            } else { // 点击关闭照明灯
                [self turnOnLight:NO];
                button.selected = NO;
            }
        }
        - (void)turnOnLight:(BOOL)on {
            self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
            if ([_device hasTorch]) {
                [_device lockForConfiguration:nil];
                if (on) {
                    [_device setTorchMode:AVCaptureTorchModeOn];
                } else {
                    [_device setTorchMode: AVCaptureTorchModeOff];
                }
                [_device unlockForConfiguration];
            }
        }

        - (UIImageView *)scanningline {
            if (!_scanningline) {
                _scanningline = [[UIImageView alloc] init];
                _scanningline.image = [UIImage imageNamed:@"QRCodeScanningLine"];
                _scanningline.frame = CGRectMake(scanContent_X * 0.5, scanContent_Y, self.frame.size.width - scanContent_X , scanninglineHeight);
            }
            return _scanningline;
        }

        - (void)addScanningline {
            // 扫描动画添加
            [self.tempLayer addSublayer:self.scanningline.layer];
        }

        #pragma mark - - - 添加定时器
        - (void)addTimer {
            [self addScanningline];

            self.timer = [NSTimer scheduledTimerWithTimeInterval:SGQRCodeScanningLineAnimation target:self selector:@selector(timeAction) userInfo:nil repeats:YES];
            [[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
        }
        #pragma mark - - - 移除定时器
        - (void)removeTimer {
            [self.timer invalidate];
            self.timer = nil;
            [self.scanningline removeFromSuperview];
            self.scanningline = nil;
        }

        #pragma mark - - - 执行定时器方法
        - (void)timeAction {
            __block CGRect frame = _scanningline.frame;

            static BOOL flag = YES;

            if (flag) {
                frame.origin.y = scanContent_Y;
                flag = NO;
                [UIView animateWithDuration:SGQRCodeScanningLineAnimation animations:^{
                    frame.origin.y += 5;
                    _scanningline.frame = frame;
                } completion:nil];
            } else {
                if (_scanningline.frame.origin.y >= scanContent_Y) {
                    CGFloat scanContent_MaxY = scanContent_Y + self.frame.size.width - 2 * scanContent_X;
                    if (_scanningline.frame.origin.y >= scanContent_MaxY - 10) {
                        frame.origin.y = scanContent_Y;
                        _scanningline.frame = frame;
                        flag = YES;
                    } else {
                        [UIView animateWithDuration:SGQRCodeScanningLineAnimation animations:^{
                            frame.origin.y += 5;
                            _scanningline.frame = frame;
                        } completion:nil];
                    }
                } else {
                    flag = !flag;
                }
            }
        }
收藏
0
sina weixin mail 回到顶部