[iOS] 图文讲解原生二维码有效扫描区域 rectOfInterest

在使用原生的 AVFoundation 框架实现二维码扫描的时候, 需要注意一下两个方面:

    1. 启动相机的卡顿问题;
    1. 有效扫描区域的问题; 本文主要针对这两个问题进行讲解.

1. 启动扫描卡顿

Push到二维码扫描页时, 一般在初始化扫描视图的时候就开始启动session:

[self.session startRunning]

但是这样会有一个问题, 就是点击扫描按钮的时候, 按钮会1秒多的卡顿, 然后才会Push到扫描页;
针对这个问题, 有的人是在Push到扫描页的时候, 才去启动 session, 但是这样会有1 – 2秒的等待时间, 才会出现正常的扫描画面;

针对此问题, 解决的方式很简单, 依然在初始化的时候启动 session, 但是, 需要异步去启动:

[self.activity startAnimating];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        
        if (self.session.isRunning == NO) {
            NSLog(@"startRunning");
            [self.session startRunning];
        }
        
        dispatch_async(dispatch_get_main_queue(), ^{
            
            [self.activity stopAnimating];
        });
    });

这样, 在push 的过程中就在准备session, 可以很大程度上缩短等待时间, 甚至, 在push 到扫描页的时候, 几乎看不到等待时间.

需要注意

如果使用了 属性观察者模式, 其方法是异步执行的, 需要返回到主线程, 例如这里我监听了属性 running:

[self.session addObserver:self forKeyPath:@"running" options:NSKeyValueObservingOptionNew context:nil];

根据这个来开始/停止扫描线的动画, 这时候, 就需要回到主线程执行:

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context{
    
    dispatch_async(dispatch_get_main_queue(), ^{
        
        if ([object isKindOfClass:[AVCaptureSession class]]) {
            
            if ([keyPath isEqualToString:@"running"]) {
                BOOL isRunning = ((AVCaptureSession *)object).isRunning;
                if (isRunning) {
                    
                    [self startAnimate];
                }else{
                    [self stopAnimate];
                }
            }
            
        }
    });
}

2. AVCaptureMetadataOutput 有效扫描区域 rectOfInterest

首先, 需要知道 rectOfInterest 所在的坐标系, 其坐标原点是图像的左上角, 注意, 这里是图像的左上角, 不是设备的左上角, 其值介于 0 – 1, 如果设置为 CGRectMake(0, 0, 1, 1) 将是全屏幕扫描; 所以需要将我们的扫描框的坐标(相对于设备), 转换为 0–1 之间的值(相对于图像).

2.1. 影响因素

要想准确计算其区域, 就需要知道影响因素, rectOfInterest 的影响因素有以下两个:

    1. AVCaptureSessionsessionPreset 属性
    1. AVCaptureVideoPreviewLayervideoGravity 属性

前者为图像的分辨率, 不同的值生成不同分辨率的图像;
后者为预览时的图像填充模式, 类似于 UIViewcontentMode 属性;

sessionPreset

// 完整的图像分辨率输出,不支持音频
NSString *const AVCaptureSessionPresetPhoto; 
// 最高分辨率,根据设备系统自动选择最高分辨率
NSString *const AVCaptureSessionPresetHigh;
// 中等分辨率,根据设备系统自动选择中等分辨率
NSString *const AVCaptureSessionPresetMedium;
// 最低分辨率,根据设备系统自动选择最低分辨率
NSString *const AVCaptureSessionPresetLow;
// 以352x288分辨率输出
NSString *const AVCaptureSessionPreset352x288;
// 以640x480分辨率输出
NSString *const AVCaptureSessionPreset640x480;
// 以1280x720分辨率输出
NSString *const AVCaptureSessionPreset1280x720;
// 以1920x1080分辨率输出
NSString *const AVCaptureSessionPreset1920x1080;
// 以960x540分辨率输出
NSString *const AVCaptureSessionPresetiFrame960x540;
// 以1280x720分辨率输出
NSString *const AVCaptureSessionPresetiFrame1280x720;
// 不去控制音频与视频输出设置,而是通过已连接的捕获设备的 activeFormat 来反过来控制 capture session 的输出质量等级
NSString *const AVCaptureSessionPresetInputPriority;

videoGravity

// 保持原始比例,自适应最小的bounds,不足的会有留白;类似于UIView的contentMode属性的UIViewContentModeScaleAspectFit.
AVLayerVideoGravityResizeAspect;

// 保持原始比例,填充整个bounds,多余的会被剪掉,类似于UIView的contentMode属性的UIViewContentModeScaleAspectFill.
AVLayerVideoGravityResizeAspectFill;

// 拉伸直到填充整个bounds,类似于UIView的contentMode属性的UIViewContentModeSca
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值