二维码扫描

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

效果图

scan.gif

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

@interface ScanViewController () <AVCaptureMetadataOutputObjectsDelegate>
 //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];
      }
    
  • 然后就是扫描到结果 从代理<AVCaptureMetadataOutputObjectsDelegate>的实现中拿到结果
    #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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容