实用知识:实现扫描二维码和条形码功能

本文详细介绍了一个iOS应用中实现二维码扫描的过程。从初始化AVCaptureSession到配置AVCaptureDeviceInput,再到利用AVCaptureMetadataOutput进行元数据解析,最后实现扫描结果的回调处理。文中还涉及了如何设置扫描的有效范围和界面预览。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#import "ViewController.h"

// 捕获摄像头数据需要该框架
#import <AVFoundation/AVFoundation.h>

@interface ViewController () <AVCaptureMetadataOutputObjectsDelegate>

@property (strong, nonatomic) AVCaptureSession *captureSession;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // 扫描二维码: 摄像头(捕获设备) -> 捕获摄像头数据(输出) -> 捕获会话 -> 元数据解析(输出) -> 二维码结果

    // 1. 获取摄像头 (捕获视频数据) (要获取授权, 不需要写申请代码)
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    // 2. 输入流 (将摄像头数据传递给会话)
    // AVCaptureInput 输入法是基类, AVCaptureDeviceInput 专门处理设备的输入法
    NSError *error;
    AVCaptureDeviceInput *deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
    if (error) {
        NSLog(@"设备输入流创建失败");
        return;
    }

    // 3. 捕获数据的会话 (协调输入与输出)
    self.captureSession = [[AVCaptureSession alloc] init];
    // 3.1 将输入流与Session进行绑定
    if ([self.captureSession canAddInput:deviceInput]) {
        [self.captureSession addInput:deviceInput];
    }

    /*================= 界面预览 =================*/

    // AVCaptureVideoPreviewLayer 是 捕获视频的预览图层
    AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
    // 让layer显示出来
    [self.view.layer addSublayer:layer];
    // 注意: 需要手动配置layer的frame值
    layer.frame = self.view.bounds;

    /*=================  =================*/

    // 4. 输出流 (解析数据 - 对元数据解析(Metadata))
    // AVCaptureMetadataOutput 用来对捕获数据进行解析
    AVCaptureMetadataOutput *metadataOutput = [[AVCaptureMetadataOutput alloc] init];

    // 4.1 将输出流与Session进行绑定
    if ([self.captureSession canAddOutput:metadataOutput]) {
        [self.captureSession addOutput:metadataOutput];
    }

    // 4.2 配置解析的数据类型, 解析结果的回调

    // delegate是回调代理对象 queue是代理回调时处理的队列
    [metadataOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];

    // 查看可用的数据类型
    NSArray *types = metadataOutput.availableMetadataObjectTypes;
    NSLog(@"%@", types);
    // 设置解析的数据类型
    // 二维码
//    metadataOutput.metadataObjectTypes = @[@"org.iso.QRCode"];
    // 条形码
    metadataOutput.metadataObjectTypes = @[@"org.gs1.EAN-8", @"org.gs1.EAN-13", @"org.iso.Code128", @"com.intermec.Code93", @"org.iso.Code39"];

    // 解析范围, (输出), 有效的扫描范围
    CGFloat wh = 300;   // 有效范围 300 * 300, 居中
    // 参数值是 0.0 ~ 1.0, Rect:(y,x,height,width)
    CGFloat width = wh / self.view.bounds.size.width;
    CGFloat height = wh / self.view.bounds.size.height;
    CGFloat x = (self.view.bounds.size.width - wh) * 0.5 / self.view.bounds.size.width;
    CGFloat y = (self.view.bounds.size.height - wh) * 0.5 / self.view.bounds.size.height;
    // Rect:(y,x,height,width)
    metadataOutput.rectOfInterest = CGRectMake(y, x, height, width);

    // 5. 启动会话, 开始工作
    [self.captureSession startRunning];

    [self addOtherLay:CGRectMake((self.view.bounds.size.width - wh) * 0.5, (self.view.bounds.size.height - wh) * 0.5, wh, wh)];

//    // CAShapeLayer 绘制形状的图层, 灰色图层
//    CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
////    shapeLayer.frame = CGRectMake(0, 0, self.view.bounds.size.width, 200);
//    shapeLayer.path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, self.view.bounds.size.width, 200)].CGPath;
//    shapeLayer.fillColor = [UIColor blackColor].CGColor;
//    shapeLayer.opacity = 0.5;
//    [self.view.layer addSublayer:shapeLayer];
}

#pragma mark - AVCaptureMetadataOutputObjectsDelegate

// 解析到数据时触发 (解析到二维码), 只要会话运行并且扫描到数据, 就会一直触发
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    AVMetadataMachineReadableCodeObject *metadataObject = metadataObjects.lastObject;

    NSString *result = metadataObject.stringValue;
    NSLog(@"%@", result);

    // 字符串, 对字符串处理
    // http://121.42.205.189/index.php?cid=2063   http://开头
    // Weixin:/dgiojaogjasd         // weixin: 微信功能

    // 停止会话
    [self.captureSession stopRunning];
}

// 添加遮盖
- (void)addOtherLay:(CGRect)rect
{
    // Rect为保留的矩形frame值
    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
    CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
    CGFloat leftWidth = (screenWidth - rect.size.width) / 2;

    CAShapeLayer* layerTop   = [[CAShapeLayer alloc] init];
    layerTop.fillColor       = [UIColor blackColor].CGColor;
    layerTop.opacity         = 0.5;
    layerTop.path            = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, screenWidth, rect.origin.y)].CGPath;
    [self.view.layer addSublayer:layerTop];

    CAShapeLayer* layerLeft   = [[CAShapeLayer alloc] init];
    layerLeft.fillColor       = [UIColor blackColor].CGColor;
    layerLeft.opacity         = 0.5;
    layerLeft.path            = [UIBezierPath bezierPathWithRect:CGRectMake(0, rect.origin.y, leftWidth, rect.size.height)].CGPath;
    [self.view.layer addSublayer:layerLeft];

    CAShapeLayer* layerRight   = [[CAShapeLayer alloc] init];
    layerRight.fillColor       = [UIColor blackColor].CGColor;
    layerRight.opacity         = 0.5;
    layerRight.path            = [UIBezierPath bezierPathWithRect:CGRectMake(screenWidth - leftWidth, rect.origin.y, rect.size.width, rect.size.height)].CGPath;
    [self.view.layer addSublayer:layerRight];

    CAShapeLayer* layerBottom   = [[CAShapeLayer alloc] init];
    layerBottom.fillColor       = [UIColor blackColor].CGColor;
    layerBottom.opacity         = 0.5;
    layerBottom.path            = [UIBezierPath bezierPathWithRect:CGRectMake(0, CGRectGetMaxY(rect), screenWidth, screenHeight - CGRectGetMaxY(rect))].CGPath;
    [self.view.layer addSublayer:layerBottom];
}

@end
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值