iOS 原生AVFoundation 限制区域二维码扫描

1.导入AVFoundation/AVFoundation.h框架

2.在初始化时 定义imageView 作为自定义的现实区域

3.绘制遮盖层 可视区域之外的部分 半透明的遮盖  默认扫描二维码是整个手机屏幕的

4.配置 device  input  output session 相关信息

5.设置output的现实区域 这里要注意 坐标原点于默认的相反 x 是以前的y  类似 宽高 并且这里是0-1之间的 比例数字



直接上代码


#import "SecondViewController.h"
#import <AVFoundation/AVFoundation.h>

@interface SecondViewController ()<AVCaptureMetadataOutputObjectsDelegate>

@property (strong,nonatomic) AVCaptureDevice * device;

@property (strong,nonatomic) AVCaptureDeviceInput * input;

@property (strong,nonatomic) AVCaptureMetadataOutput * output;

@property (strong,nonatomic) AVCaptureSession * session;

@property (strong,nonatomic) AVCaptureVideoPreviewLayer * preview;

@property (nonatomic,strong) UIImageView *animationView;
@property (nonatomic,strong) NSTimer *timer;

@property (nonatomic,strong) UIImageView *imageView;
@property (nonatomic,strong) CALayer *maskLayer;
@property (nonatomic,assign) CMVideoDimensions dimensions;

@end

@implementation SecondViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //扫描的边框
    UIImage *image = [UIImage imageNamed:@"qrcode_border"];
    //这里是剪切拉伸一个图片(把四角拉出去 内部内容填充)
    UIImage *boundImage = [image resizableImageWithCapInsets:UIEdgeInsetsMake(25, 25, 26, 26)];
    self.imageView = [[UIImageView alloc] initWithImage:boundImage];
    
    //左边从70px开始
    CGFloat width = [UIScreen mainScreen].bounds.size.width - 70 *2;
    [self.imageView setFrame:CGRectMake(70, 150, width, width)];
    [self.view addSubview:self.imageView];
    
    //框中间动画效果的图片
    self.animationView = [[UIImageView alloc] initWithFrame:CGRectMake(0, -width, width, width)];
    [self.animationView setImage:[UIImage imageNamed:@"qrcode_scanline_qrcode"]];
    [self.imageView addSubview:self.animationView];
    self.imageView.clipsToBounds = YES;
    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.02f target:self selector:@selector(changeImage:) userInfo:nil repeats:YES];

    
}

-(void)changeImage:(NSTimer *)timer{
    
    //更新图片产生动画
    self.animationView.frame = CGRectOffset(self.animationView.frame, 0, 2);
    
    if (self.animationView.frame.origin.y >= self.animationView.superview.frame.size.height) {
        self.animationView.frame = CGRectMake(0, -CGRectGetHeight(self.animationView.superview.frame), self.animationView.frame.size.width, self.animationView.frame.size.height);
    }
}


- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    [self begining];
}

- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [self endRead:nil];
}

- (void)begining{
    
    self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    NSError *error;
    self.input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:&error];
    self.output = [[AVCaptureMetadataOutput alloc] init];
    self.session = [[AVCaptureSession alloc] init];
    
    //配置session相关联系
    if ([_device supportsAVCaptureSessionPreset:AVCaptureSessionPreset1920x1080]) {
        _session.sessionPreset = AVCaptureSessionPreset1920x1080;//设置图像输出质量
        self.dimensions = (CMVideoDimensions){1080,1920};
    }else if ([_device supportsAVCaptureSessionPreset:AVCaptureSessionPreset1280x720]){
        _session.sessionPreset = AVCaptureSessionPreset1280x720;
        self.dimensions = (CMVideoDimensions){720,1280};
    }else if ([_device supportsAVCaptureSessionPreset:AVCaptureSessionPreset640x480]){
        _session.sessionPreset = AVCaptureSessionPreset640x480;
        self.dimensions = (CMVideoDimensions){480,640};
    }else if ([_device supportsAVCaptureSessionPreset:AVCaptureSessionPreset352x288]){
        _session.sessionPreset = AVCaptureSessionPreset352x288;
        self.dimensions = (CMVideoDimensions){288,352};
    }
    
    
    //这里必须真机 需要相关 相机等等设备 使用时打开这行注释就好
    [self.session addInput:self.input];
    [self.session addOutput:self.output];
    //配置output信息
    //设置delegate,输出的类型
    [self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    [self.output setMetadataObjectTypes:self.output.availableMetadataObjectTypes];
    
    // 5. 设置预览图层(用来让用户能够看到扫描情况)
    
    AVCaptureVideoPreviewLayer *preview = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
    
    // 5.1 设置preview图层的属性
    
    [preview setVideoGravity:AVLayerVideoGravityResizeAspectFill];
    
    // 5.2 设置preview图层的大小
    
    [preview setFrame:self.view.bounds];
    
    preview.backgroundColor = [UIColor lightGrayColor].CGColor;
    
    // 5.3 将图层添加到视图的图层
    
    [self.view.layer insertSublayer:preview atIndex:0];

    self.maskLayer = [[CALayer alloc]init];
    self.maskLayer.frame = self.view.layer.bounds;
    self.maskLayer.delegate = self;
    [self.view.layer insertSublayer:self.maskLayer above:preview];
    [self.maskLayer setNeedsDisplay];
    
    
    
    /*获取图像输出大小*/
    CMVideoDimensions dimensions = self.dimensions;
    CGFloat width = dimensions.width;
    CGFloat height = dimensions.height;
    
    CGSize size = self.view.bounds.size;
    
    CGFloat p1 = size.height/size.width;
    CGFloat p2 = height/width; //与之前设置的图像输出质量应该对应。
    CGFloat viewWidth = [UIScreen mainScreen].bounds.size.width - 70 *2;
    if (p1 < p2) {
        CGFloat fixHeight = self.view.bounds.size.width * height / width; //缩放并剪裁后的高度。
        CGFloat fixPadding = (fixHeight - size.height)/2;
        _output.rectOfInterest = CGRectMake((150 + fixPadding)/fixHeight,
                                           70/size.width,
                                           viewWidth/fixHeight,
                                           viewWidth/size.width);
    } else {
        CGFloat fixWidth = self.view.bounds.size.height * width / height;
        CGFloat fixPadding = (fixWidth - size.width)/2;
        _output.rectOfInterest = CGRectMake(150/size.height,
                                           (70 + fixPadding)/fixWidth,
                                           viewWidth/size.height,
                                           viewWidth/fixWidth);
    }

    // 6. 启动会话
    [self.session startRunning];
    [self.timer fire];
}

#pragma mark AVCaptureMetadataOutputObjectsDelegate

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
    
    NSString *stringValue;
    
    if ([metadataObjects count] >0){
        //停止扫描
        [_session stopRunning];
        
        AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjects objectAtIndex:0];
        
        stringValue = metadataObject.stringValue;
        if ([metadataObject isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) {
            //在主线程中刷新UI
            [self performSelectorOnMainThread:@selector(endRead:) withObject:stringValue waitUntilDone:YES];
        }
        
    }
    
}

//Note: 蒙板生成。需设置代理,并在退出页面时取消代理。
-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
        UIGraphicsBeginImageContextWithOptions(layer.frame.size, NO, 1.0);
        CGContextSetFillColorWithColor(ctx, [UIColor colorWithRed:0 green:0 blue:0 alpha:0.6].CGColor);
        CGContextFillRect(ctx, layer.frame);
        CGRect scanFrame = [self.view convertRect:self.imageView.frame fromView:self.imageView.superview];
        CGContextClearRect(ctx, scanFrame);
}

- (void)endRead:(id)userInfo{
    [self.session stopRunning];
    [self.timer invalidate];
    self.maskLayer.delegate = nil;
    
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"二维码" message:userInfo preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
        
        [self.navigationController popViewControllerAnimated:YES];
        
    }]];
    
    [self presentViewController:alertController animated:YES completion:nil];
}

@end

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值