年年有"余"

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 3677|回复: 1

[SAFoundation] 利用系统AV框架制作实现扫一扫功能(一行代码搞定)

[复制链接]
  • TA的每日心情

    2024-10-15 10:05
  • 签到天数: 372 天

    [LV.9]以坛为家II

    发表于 2015-12-3 17:54:18 | 显示全部楼层 |阅读模式
    本帖最后由 Sian 于 2015-12-3 17:55 编辑

    1、先上图看看效果

    Screen-Shot-2015-12-03-at-下午5.32.13.jpg


    2、设计思路
    2.1、利用AVCaptureMetadataOutput固有的输出属性,能够输出二维码及条形码的解析结果
    2.2、利用Quartz 2D绘出基本扫描界面,结合动画效果,简单又高效!2.3、只需两个类,一个控制器,一个视图:SAScanCtrl   SAScan
    2.3、其他内容在代码中给出相关注释说明

    3、部分代码
    SAScanCtrl.h

    [Objective-C] 纯文本查看 复制代码
    //
    //  SAScanCtrl.h
    // 
    //
    //  Created by 余西安 on 15/12/3.
    //  Copyright © 2015年 Sian. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    
    typedef void (^SAScanBlock)(NSString *string);
    
    @interface SAScanCtrl : UIViewController
    
    @property (nonatomic, copy) SAScanBlock block;
    
    - (instancetype)initWithBlock:(SAScanBlock)block;
    
    @end
    
    SAScanCtrl.m
    [Objective-C] 纯文本查看 复制代码
    //
    //  SAScanCtrl.m
    //
    //
    //  Created by 余西安 on 15/12/3.
    //  Copyright &#169; 2015年 Sian. All rights reserved.
    //
    
    #import "SAScanCtrl.h"
    #import "SAScan.h"
    #import <AVFoundation/AVFoundation.h>
    
    @interface SAScanCtrl ()<AVCaptureMetadataOutputObjectsDelegate>
    
    @property (nonatomic, strong) AVCaptureSession *session;
    
    @property (nonatomic, strong) AVAudioPlayer    *player;
    
    @property (nonatomic, strong) SAScan           *scan;
    
    @end
    
    @implementation SAScanCtrl
    
    - (instancetype)initWithBlock:(SAScanBlock)block
    {
        if (self = [super init]){
            self.block = block;
        }
        return self;
    }
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        [self.navigationItem setTitle:@"扫一扫"];
        
        AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
        AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc]init];
        [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];   // 设置代理 在主线程里刷新
        [output setRectOfInterest:CGRectMake(0.12 , 0.12, 0.8, 0.8)];
        
        //初始化链接对象
        self.session = [[AVCaptureSession alloc] init];
        [self.session addInput:input];
        [self.session addOutput:output];
        [self.session setSessionPreset:AVCaptureSessionPresetHigh];
        //设置扫码支持的编码格式(如下设置条形码和二维码兼容)
        [output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code]];
        
        AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
        layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
        layer.frame = self.view.bounds;
    
        [self.view.layer insertSublayer:layer atIndex:0];
        self.scan = [[SAScan alloc] initWithFrame:self.view.bounds];
        [self.view addSubview:self.scan];
        
        NSURL *url = [NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"scan" ofType:@"mp3"]];
        self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
        
        UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithTitle:@"重试" style:UIBarButtonItemStylePlain target:self action:@selector(startRunning)];
        [self.navigationItem setRightBarButtonItem:item];
    }
    
    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
        [self.session startRunning];
    }
    
    - (void)viewDidAppear:(BOOL)animated
    {
        [super viewDidAppear:animated];
        [self.scan startAnimation];
    }
    
    - (void)viewWillDisappear:(BOOL)animated
    {
        [super viewWillDisappear:animated];
        [self.session stopRunning];
    }
    
    - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
    {
        if (metadataObjects.count > 0){
            AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects firstObject];
            [self.player play];
            [self.scan stopAnimation];
            [self.session stopRunning];
            if (self.block) self.block(metadataObject.stringValue);
            [self.navigationController popViewControllerAnimated:YES];
            [self dismissViewControllerAnimated:YES completion:nil];
        }
    }
    
    - (void)startRunning
    {
        [self.session startRunning];
        [self.scan startAnimation];
    }
    @end
    
    Scan.h
    [Objective-C] 纯文本查看 复制代码
    //
    //  SAScan.h
    //
    //
    //  Created by 余西安 on 15/12/3.
    //  Copyright &#169; 2015年 Sian. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    
    @interface SAScan : UIView
    
    @property (nonatomic, strong) UIImageView *scanLine;
    
    - (void)startAnimation;
    
    - (void)stopAnimation;
    
    @end
    
    Scan.m
    [Objective-C] 纯文本查看 复制代码
    //
    //  SAScan.m
    //
    //
    //  Created by 余西安 on 15/12/3.
    //  Copyright &#169; 2015年 Sian. All rights reserved.
    //
    
    #import "SAScan.h"
    
    @implementation SAScan
    
    - (instancetype)initWithFrame:(CGRect)frame
    {
        if (self = [super initWithFrame:frame]){
            self.backgroundColor = [UIColor clearColor];
            UIImage *line = [UIImage imageNamed:@"scanLine.png"];
            self.scanLine = [[UIImageView alloc] initWithImage:line];
            [self.scanLine setHidden:YES];
            [self addSubview:self.scanLine];
        }
        return self;
    }
    
    - (void)drawRect:(CGRect)rect
    {
        CGContextRef context = UIGraphicsGetCurrentContext();
        //非扫码区域半透明
        {
            CGFloat w = self.bounds.size.width * 0.7;
            CGFloat x = (self.bounds.size.width - w) * 0.5;
            CGFloat y = (self.bounds.size.height - w) * 0.5;
            {   // 外围区域填充半透明黑色
                // 设置非识别区域颜色
                CGContextSetRGBFillColor(context, 0, 0, 0, 0.6);
                
                // 扫码区域上面填充
                CGRect rect = CGRectMake(0, 0, self.frame.size.width, y);
                CGContextFillRect(context, rect);
                
                // 扫码区域左边填充
                rect = CGRectMake(0, y, x, w);
                CGContextFillRect(context, rect);
                
                // 扫码区域右边填充
                rect = CGRectMake(x + w, y, x, w);
                CGContextFillRect(context, rect);
                
                // 扫码区域下面填充
                rect = CGRectMake(0, y + w, self.frame.size.width, self.frame.size.height - y - w);
                CGContextFillRect(context, rect);
            }
            
            {   // 中间可视区域画边框
                UIColor *whiteColor = [UIColor colorWithWhite:1.0 alpha:0.5];
                CGContextSetStrokeColorWithColor(context, whiteColor.CGColor);
                CGContextSetLineWidth(context, 1);
                CGContextAddRect(context, CGRectMake(x, y, w, w));
                CGContextStrokePath(context);
            }
            
            
            {   // 中间可视区域画角框
                CGFloat lineWidth = 4.0f;
                CGFloat angleWidth = 15.0f;
                UIColor *greenColor = [UIColor colorWithRed:0 green:1.0 blue:0 alpha:0.8];
                CGContextSetLineWidth(context, lineWidth);
                CGContextSetStrokeColorWithColor(context, greenColor.CGColor);
                
                // 左上角
                CGContextMoveToPoint(context, x, y + angleWidth);
                CGContextAddLineToPoint(context, x, y);
                CGContextAddLineToPoint(context, x + angleWidth, y);
                // 右上角
                CGContextMoveToPoint(context, x + w - angleWidth, y);
                CGContextAddLineToPoint(context, x + w, y);
                CGContextAddLineToPoint(context, x + w, y + angleWidth);
                // 右下角
                CGContextMoveToPoint(context, x + w, y + w - angleWidth);
                CGContextAddLineToPoint(context, x + w, y + w);
                CGContextAddLineToPoint(context, x + w - angleWidth, y + w);
                // 左下角
                CGContextMoveToPoint(context, x + angleWidth, y + w);
                CGContextAddLineToPoint(context, x, y + w);
                CGContextAddLineToPoint(context, x, y + w - angleWidth);
            }
            CGContextStrokePath(context);
        }
    }
    
    - (void)layoutSubviews
    {
        [super layoutSubviews];
        CGFloat width = self.bounds.size.width;
        CGFloat y = (self.bounds.size.height - width * 0.7) * 0.5;
        self.scanLine.frame = CGRectMake(0, y, width, 12);
    }
    
    - (void)startAnimation
    {
        CGFloat width = self.bounds.size.width;
        CGFloat y = (self.bounds.size.height - width * 0.7) * 0.5;
        self.scanLine.frame = CGRectMake(0, y, width, 12);
        self.scanLine.hidden = NO;
        [UIView animateWithDuration:2.5 delay:0 options:UIViewAnimationOptionRepeat | UIViewAnimationOptionCurveLinear animations:^{
            self.scanLine.center = CGPointMake(width * 0.5, y + width * 0.7);
        } completion:^(BOOL finished) {
            if (finished) self.scanLine.center = CGPointMake(width * 0.5, y);
        }];
    }
    
    - (void)stopAnimation
    {
        self.scanLine.hidden = YES;
        [self.scanLine.layer removeAllAnimations];
    }
    @end
    

    4、使用只需一行代码:
        SAScanCtrl *scan = [[SAScanCtrl alloc] initWithBlock:^(NSString *string) {
            [[[UIAlertView alloc] initWithTitle:nil message:string delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil] show];
        }];
        UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:scan];
        [self presentViewController:nav animated:YES completion:nil];


    5、Demo下载
    游客,如果您要查看本帖隐藏内容请回复

    该用户从未签到

    发表于 2015-12-23 15:57:09 | 显示全部楼层
    二维码扫描,,看看,,谢谢
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    手机版|小黑屋|Archiver|iOS开发笔记 ( 湘ICP备14010846号 )

    GMT+8, 2024-11-22 06:52 , Processed in 0.061974 second(s), 26 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表