二维码扫描

自用-二维码扫描模块代码

效果图

scan.gif

直接上代码 注释写的还算详细
-需要准备下面的东西,其中preView是要显示的样式(就是摄像头画面加一个扫描框,因为扫描是不需要画面的,为了用户体验)

@interface ScanViewController () 
 //1.输入设备(从外界采集信息)  输入设备很多种  摄像头  麦克风  键盘
@property (nonatomic, strong) AVCaptureDeviceInput* input;
//2.输出设备 (解析采集来得内容 然后获取到数据) Metadata 元数据
@property (nonatomic, strong) AVCaptureMetadataOutput* output;
//3.会话 session (连接输入和输出进行工作)
@property (nonatomic, strong) AVCaptureSession* session;//4.展示layer@property (nonatomic, strong) PreView* preView;
@end
  • 接下来是扫描代码
    #pragma mark--开始扫描
    - (void)startScan
    {

        //1.输入设备(从外界采集信息)
        //创建具体的设备  摄像头
        //AVMediaTypeVideo  摄像头     AVMediaTypeAudio 麦克风
       AVCaptureDevice* device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
      self.input = [AVCaptureDeviceInput deviceInputWithDevice:device error:NULL];
    
      //2.输出设备 (解析采集来得内容 然后获取到数据) Metadata 元数据
      self.output = [[AVCaptureMetadataOutput alloc] init];
    
      //3.会话 session (连接输入和输出进行工作)
    
      self.session = [[AVCaptureSession alloc] init];
    
      //会话扫描展示的大小
      [self.session setSessionPreset:AVCaptureSessionPresetHigh];
      //添加输入设备和输出设备
      if ([self.session canAddInput:self.input]) {
          [self.session addInput:self.input];
      }
      if ([self.session canAddOutput:self.output]) {
          [self.session addOutput:self.output];
      }
    
      //指定输出设备的 代理   来返回 解析到得数据
      [self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    
      //设置元数据类型 QRCode 二维码+条形码
      [self.output setMetadataObjectTypes:@[ AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code ]];
    
      //.创建特殊视图展示二维码界面
      self.preView = [[PreView alloc] initWithFrame:self.view.bounds];
      self.preView.session = self.session;
      [self.view addSubview:self.preView];
    
      //设置扫描区域(我设置的中间靠上w:300 h:300,这样不会打开就扫描上,用户体验会好点)-需要先设置frame 然后转化添加到上去
      CGRect rect = CGRectMake(30, 100, 300, 300);
      CGRect interRect = [self.preView.previewLayer metadataOutputRectOfInterestForRect:rect];
      self.output.rectOfInterest = interRect;
    
      //5.开启 会话
      [self.session startRunning];
      }
    
  • 然后就是扫描到结果 从代理的实现中拿到结果
    #pragma mark--扫描成功 二维码代理--
    /**
    * 解析出元数据就会调用
    *
    * @param captureOutput 输出
    * @param metadataObjects 元数据数组
    * @param connection 连接
    /
    - (void)captureOutput(AVCaptureOutput
    )captureOutput didOutputMetadataObjects:(NSArray)metadataObjects fromConnection:(AVCaptureConnection)connection
    {
    //结束扫描
    [self stopScan];
    NSMutableString* str = [NSMutableString string];
    for (AVMetadataMachineReadableCodeObject* objc in metadataObjects) {
    [str appendString:objc.stringValue];
    }
    //打印
    NSLog(@"%@",(NSString *)str);
    [self.navigationController popViewControllerAnimated:YES];
    }
    -结束扫描的代码
    #pragma mark--结束扫描
    - (void)stopScan
    {
    //停止会话
    [self.session stopRunning];
    //移除特殊的图层
    [self.preView removeFromSuperview];
    }

  • 接下来需要注意的就是preView的实现 向微信扫描一样,有一个半透明的遮罩,中间有一个透明区域,这个地方用layer的mask实现,扫描动画,使用定时器实现,上代码
    @interface PreView ()
    @property(nonatomic,strong)UIImageView bgImage;
    //添加线
    @property (nonatomic, strong) UIImageView
    lineImageView;
    //定时器
    @property (nonatomic, strong) CADisplayLink* displayLink;
    @end

    @implementation PreView
    /**
    *  layer
    *  @return 返回AVCaptureVideoPreviewLayer 特殊的layer 可以展示输入设备采集到的信息
    */
    +(Class)layerClass{
        return [AVCaptureVideoPreviewLayer class];
     }
    
    -(AVCaptureVideoPreviewLayer *)previewLayer{
    
        AVCaptureVideoPreviewLayer *layer = (AVCaptureVideoPreviewLayer *)self.layer;
        return layer;
    }
    
    -(void)setSession:(AVCaptureSession *)session{
    
        _session = session;
        AVCaptureVideoPreviewLayer *layer = (AVCaptureVideoPreviewLayer *)self.layer;
        layer.session = session;
    }
    - (instancetype)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
           [self setMask];
        }
        return self;
    }
    -(void)setMask{
       //添加扫描线
       self.lineImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"line"]];
       self.lineImageView.frame = CGRectMake((kScreenWidth - 280) / 2, 100, 280, 2);
       [self addSubview:self.lineImageView];
       //计时器
       self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(scanAnimation)];
      [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
       //创建一个maskView
       UIView *maskView = [[UIView alloc]initWithFrame:CGRectMake(0, 64, kScreenWidth, kScreenHigh-64)];
       //颜色设置透明
        maskView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
        [self addSubview:maskView];
        //创建一个中间有镂空区域的path
        UIBezierPath *maskPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, kScreenWidth, kScreenHigh-64)];
        [maskPath appendPath:[[UIBezierPath bezierPathWithRoundedRect:CGRectMake((kScreenWidth-280)/2, 100-64, 280, 280) cornerRadius:1] bezierPathByReversingPath]];
    
       CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
       maskLayer.path = maskPath.CGPath;
       maskView.layer.mask = maskLayer;
      //添加扫描框
      [self addSubview:self.bgImage];
      _bgImage.frame = CGRectMake((kScreenWidth-280)/2, 100, 280, 280);
    }
    
     #pragma mark--扫描动画--
    - (void)scanAnimation
    {
      CGRect frame = self.lineImageView.frame;
      if (self.lineImageView.frame.origin.y > 100 + 270) {
         frame.origin.y = 100;
         self.lineImageView.frame = frame;
         } else {
          frame.origin.y += 2;
          self.lineImageView.frame = frame;
                 }
    }
    
     #pragma mark --懒加载
     -(UIImageView *)bgImage{ 
       if (!_bgImage) {
      _bgImage = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"icon_scanbg"]];
        }
       return _bgImage;
    }
    @end
    
  • 差不多 这样就搞定了,需要注意的是preView的加载和移除 还有扫描方法的加载时间,发现如果Push出来的窗口,二维码界面打开会有一秒多延迟,建议加一个MBProgress过度,如果Model的话,就没有这个问题(执行扫描我在viewWillAppear中加载)

附上github地址:https://github.com/superHS/Demo.git

你可能感兴趣的:(二维码扫描)