美文网首页很常iOS常用
IOS 原生二维码扫描

IOS 原生二维码扫描

作者: 本客 | 来源:发表于2021-04-26 16:25 被阅读0次

    前文:

     今天来详细写一下扫一扫实现,基本上实现扫一扫有三个库,分别是ZBar,ZXing,AVFoundation,在IOS7以前是没有AVFoundation的。

     1.ZBar在扫描灵敏度和内存上比ZXing较好,缺点却很多,比如有圆角的二维码很难扫。目前已经停止更新

     2.ZXing是GoogleCode上的一个开源的条形码扫码库,是用java设计的,连Google Glass都在使用。但有人为了追求更高效率以及可移植性,出现了c++ port. Github上的Objectivc-C port,其实就是用OC代码封装了一下而已,而且已经停止维护。

     3.AVFoundation提供原生api扫描二维码,无论在扫描灵敏度和性能上来说都是最优的,所以毫无疑问我们应该切换到AVFoundation,但是只兼容IOS7及以上,如果是IOS7之前的版本就要使用ZBar或ZXing代替。

    我写的比较全面,二维码,条形码,扫描区域设置,光感传感器应用,闪光灯显示与隐藏,闪光灯打开与关闭,相册获取二维码扫描


    头文件:

     AVFoundation 是扫描需要用到的库

     ImageIO 是闪光灯需要用到的库

    #import <AVFoundation/AVFoundation.h>

    #import <ImageIO/ImageIO.h>

    代理协议:

     AVCaptureMetadataOutputObjectsDelegate 扫描二维码结果代理

     AVCaptureVideoDataOutputSampleBufferDelegate 周围环境光感传感器代理

     UINavigationControllerDelegate、UIImagePickerControllerDelegate 选择相册图片代理

    <AVCaptureMetadataOutputObjectsDelegate,AVCaptureVideoDataOutputSampleBufferDelegate,UINavigationControllerDelegate,UIImagePickerControllerDelegate>

    属性:

    //显示图层

    @property(nonatomic,strong)AVCaptureVideoPreviewLayer *layer;

    //捕捉会话

    @property(nonatomic,strong)AVCaptureSession *session;

    //辅助区域框

    @property(nonatomic,strong)UIView * cyanView;

    //打开灯光btn,默认为hidden

    @property(nonatomic,strong)UIButton * lightBtn;

    //获取相册图片进行扫描

    @property(nonatomic,strong)UIButton * photoBtn;

    //建议先判断一下,是否开启相机权限

    //创建状态对象

    AVAuthorizationStatus authStatus =[AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];

    //判断摄像头状态是否可用

        if(authStatus==AVAuthorizationStatusAuthorized){

            //开始扫描二维码

            [self startScan];

        }else{

            NSLog(@"未开启相机权限,请前往设置中开启");

        }

    //开始扫描二维码

    -(void)startScan{

    //1.创建捕捉会话,AVCaptureSession是第一个要被创建的对象,所有的操作都要基于这一个session

        self.session = [[AVCaptureSession alloc]init];

        //2.添加输入源(数据从摄像头输入)

        /*输入源对应的类是AVCaptureInput,该类是一个抽象类,不能被直接实例化

        在实际使用中,都是使用他的子类,比如

        AVCaptureDeviceInput,

        AVCaptureScreenInput(只能用于Mac),

        AVCaptureMetadataInput,

        一般情况下,我们是使用AVCaptureDeviceInput,比如从设备的摄像头或者麦克风输入。

         AVCaptureDeviceInput的实例化方法如下

        */

        AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

        AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];

        [self.session addInput:input];

        //3.添加输出数据(示例对象-->类对象-->元类对象-->根元类对象)

        /*输入的类是AVCaptureInput,那么输出的类相应的就应该是AVCaptureOutput。

         输出不需要和设备挂钩,因为一般情况下,我们的输出要么是音频或视频文件,要么是一些其他的数据,像二维码扫描一般是字符串类型。

         所以创建AVCaptureOutput实例就不需要AVCaptureDevice对象。

         AVCaptureOutput也同样是一个抽象类,同样要使用其子类,在这里我们扫描二维码,

         使用的是AVCaptureMetadataOutput,设置代码如下所示*/

        AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];

        [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];

        //设置能扫描的区域,这里注意,CGRectMake的x,y和width,height的值是互换位置

        output.rectOfInterest=CGRectMake(150/self.view.frame.size.height, 100/self.view.frame.size.width, (self.view.frame.size.width-200)/self.view.frame.size.width, (self.view.frame.size.width-200)/self.view.frame.size.width);

        [self.session addOutput:output];

        //设置输入元数据的类型(类型是二维码,条形码数据,注意,这个一定要写在添加到session后面,不然要崩溃,如果只需要扫描二维码只需要AVMetadataObjectTypeQRCode,如果还需要扫描条形码,那么全部添加上)

        [output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode,

                                         AVMetadataObjectTypeEAN8Code,

                                         AVMetadataObjectTypeEAN13Code,

                                         AVMetadataObjectTypeCode39Code,

                                         AVMetadataObjectTypeCode39Mod43Code,

                                         AVMetadataObjectTypeCode93Code,

                                         AVMetadataObjectTypeCode128Code,

                                         AVMetadataObjectTypePDF417Code,

                                         AVMetadataObjectTypeAztecCode,

                                         AVMetadataObjectTypeUPCECode,

                                         AVMetadataObjectTypeInterleaved2of5Code,

                                         AVMetadataObjectTypeITF14Code,

                                         AVMetadataObjectTypeDataMatrixCode,

                                                                           ]];

        //4.添加扫描图层

        self.layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];

        self.layer.videoGravity=AVLayerVideoGravityResizeAspectFill;

        self.layer.frame = self.view.bounds;

        [self.view.layer addSublayer:self.layer];

        //5.创建view,通过layer层进行设置边框宽度和颜色,用来辅助展示扫描的区域

        UIView * cyanView=[[UIView alloc] initWithFrame:CGRectMake(100, 150, self.view.frame.size.width-200, self.view.frame.size.width-200)];

        cyanView.layer.borderWidth=2;

        cyanView.layer.borderColor =[UIColor cyanColor].CGColor;

        [self.view addSubview:cyanView];

        //6.创建检测光感源

        AVCaptureVideoDataOutput *guangOutPut = [[AVCaptureVideoDataOutput alloc] init];

        [guangOutPut setSampleBufferDelegate:self queue:dispatch_get_main_queue()];

        //设置为高质量采集率

        [self.session setSessionPreset:AVCaptureSessionPresetHigh];

        //把光感源添加到会话

        [self.session addOutput:guangOutPut];

        //7.闪光灯开关btn

        self.lightBtn=[[UIButton alloc]initWithFrame:CGRectMake(150, cyanView.frame.origin.y+cyanView.frame.size.height+100, self.view.frame.size.width-300, self.view.frame.size.width-300)];

        self.lightBtn.backgroundColor=[UIColor grayColor];

        [self.lightBtn addTarget:self action:@selector(lightBtnClick:) forControlEvents:UIControlEventTouchUpInside];

        [self.view addSubview:self.lightBtn];

        //8.获取相册图片进行扫描btn

        self.photoBtn=[[UIButton alloc]initWithFrame:CGRectMake(100, self.lightBtn.frame.origin.y+self.lightBtn.frame.size.height+50, self.view.frame.size.width-200, 50)];

        [self.photoBtn setTitle:@"相册" forState:UIControlStateNormal];

        [self.photoBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];

        self.photoBtn.backgroundColor=[UIColor orangeColor];

        [self.photoBtn addTarget:self action:@selector(photoBtnClick:) forControlEvents:UIControlEventTouchUpInside];

        [self.view addSubview:self.photoBtn];

        //9.开始扫描

        [self.session startRunning];

    }

    //实现扫描的回调代理方法

    - (void)captureOutput:(AVCaptureOutput*)captureOutput didOutputMetadataObjects:(NSArray*)metadataObjects fromConnection:(AVCaptureConnection*)connection{

    //如果数组metadataObjects中有数据,metadataObjects是个数组类型

        if(metadataObjects.count>0) {

            // 获取最终的读取结果,获取数组中最后一个元素,数组中是AVMetadataMachineReadableCodeObject类型对象

            AVMetadataMachineReadableCodeObject*object = [metadataObjects lastObject];

            NSLog(@"%@",object.stringValue);

            //停止扫描

            [self.session stopRunning];

            //移除扫描层layer

            [self.layer removeFromSuperlayer];

        }else{

            NSLog(@"没有扫描到数据");

        }

    }

    //光感传感器代理

    -(void)captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection{

        //获取光线的值

        CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL,sampleBuffer, kCMAttachmentMode_ShouldPropagate);

        NSDictionary *metadata = [[NSMutableDictionary alloc] initWithDictionary:(__bridge NSDictionary*)metadataDict];

        CFRelease(metadataDict);

        NSDictionary *exifMetadata = [[metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary] mutableCopy];

        floatbrightnessValue = [[exifMetadata objectForKey:(NSString*)kCGImagePropertyExifBrightnessValue]floatValue];

        NSLog(@"%f",brightnessValue);

        // 根据brightnessValue的值来打开和关闭闪光灯,一般值小于0就需要打开,大于0就关闭

        if((brightnessValue <0)) {//显示闪光灯

            self.lightBtn.hidden=NO;

        }elseif((brightnessValue >0)) {//隐藏闪光灯

            self.lightBtn.hidden=YES;

        }

    }

    //闪光灯按钮点击方法

    -(void)lightBtnClick:(UIButton*)sender{

        //判断当前设备是否有闪光灯

        AVCaptureDevice * device=[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

        BOOL result=[device hasTorch];

        if(result==YES){

            if(self.lightBtn.isSelected==NO){

                self.lightBtn.selected=YES;

                self.lightBtn.backgroundColor=[UIColor greenColor];

                [device lockForConfiguration:nil];

                [device setTorchMode: AVCaptureTorchModeOn];//开

                [device unlockForConfiguration];

            }else if(self.lightBtn.isSelected==YES){

                self.lightBtn.selected=NO;

                self.lightBtn.backgroundColor=[UIColor grayColor];;

                [device lockForConfiguration:nil];

                [device setTorchMode: AVCaptureTorchModeOff];//关

                [device unlockForConfiguration];

            }

        }else{

            NSLog(@"当前设备闪光灯不可用");

        }

    }

    //获取相册图片btn点击方法

    -(void)photoBtnClick:(UIButton*)sender{

        UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];

        imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;

        // imagePicker.allowsEditing = YES;

        imagePicker.delegate=self;

        [self.navigationController presentViewController:imagePicker animated:YES completion:nil];

    }

    //UIImagePickerControllerDelegate选择图片的回调

    -(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *)info {

     //把UIimage类型转换成CIimage类型

        UIImage *pickedImage = info[UIImagePickerControllerEditedImage] ?: info[UIImagePickerControllerOriginalImage];

        CIImage*detectImage = [CIImage imageWithData:UIImagePNGRepresentation(pickedImage)];

        //解析扫描二维码结果字符串

        CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyLow}];

        CIQRCodeFeature*feature = (CIQRCodeFeature*)[detector featuresInImage:detectImage options:nil].firstObject;

        [picker dismissViewControllerAnimated:YES completion:^{

            if(feature.messageString) {

                NSLog(@"=================二维码结果为%@",feature.messageString);

            }else{

                NSLog(@"未扫描到相应二维码");

            }

            //停止会话对象扫描

            [self.session stopRunning];

            //移除扫描层layer

            [self.layer removeFromSuperlayer];

        }];

    }

    相关文章

      网友评论

        本文标题:IOS 原生二维码扫描

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