条形码扫描

闲暇时间做了个条形码扫描的功能:包括界面的搭建(只能在矩形框内扫描出结果,并且中间清晰 周边模糊效果)、功能的实现。

条形码扫描_第1张图片
界面展示.jpg
一、利用AVFoundation框架属性设置(5个重要的变量):

<记录变量>
// 输入输出的中间桥梁
@property(nonatomic, strong) AVCaptureSession *session;
// layer
@property(nonatomic, strong) AVCaptureVideoPreviewLayer *layer;
@property(nonatomic, strong) AVCaptureMetadataOutput *output;

1、 初始化设备为视频设备 AVCaptureDevice *device
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
2、初始化输入设备是摄像头视频设备 AVCaptureDeviceInput *input
NSError *error = nil;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];

// 如果没有摄像头,或者摄像头不可用就调用代理方法传出错误信息
if (error) {
    if ([self.delegate respondsToSelector:@selector(openFailureWithError:)]) {
        [self.delegate openFailureWithError:error];
        NSLog(@"您的手机不支持条形码扫描");
    }
    return;
}
3、初始化输出设备 AVCaptureMetadataOutput *output
    AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
    // 设置输出设备的代理 为当前控制器 并且设置队列为主队列
    [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    self.output  = output;
4、初始化会话连接对象 AVCaptureSession *session
    self.session = [[AVCaptureSession alloc] init];
    // 高质量采集率
    [_session setSessionPreset:AVCaptureSessionPresetHigh];
    [_session addInput:input];
    [_session addOutput:output];
    
    // 设置扫码支持的编码格式(如下设置为全部条形码的可能)
    output.metadataObjectTypes = @[AVMetadataObjectTypeEAN13Code,
                                   AVMetadataObjectTypeEAN8Code,
                                   AVMetadataObjectTypeUPCECode,
                                   AVMetadataObjectTypeCode39Code,
                                   AVMetadataObjectTypeCode39Mod43Code,
                                   AVMetadataObjectTypeCode93Code,
                                   AVMetadataObjectTypeCode128Code,
                                   AVMetadataObjectTypePDF417Code];
5、用会话初始化一个能拍照扫描的AVCaptureVideoPreviewLayer *layer 大小是导航条除外屏幕的大小--size是传入的参数代表着想照射的范围,本例设置为屏幕的大小
    AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:_session];
    layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    // 设置layer的frame
    layer.frame = CGRectMake((SCREENWIDTH - size.width) * 0.5, 64, size.width, size.height - 64);
    // 把拍摄的layer添加到主视图的layer
    [self.view.layer  addSublayer:layer];
    self.layer = layer;
    
    // 开始捕获图像
    [_session startRunning];
二、界面的搭建(内清外模糊,并且只在矩形框内才能扫描出结果----抽取自定义视图)

<保存自定义视图>
@property(nonatomic, weak) PLScanView *scanView; // 扫描视图
// 视图布局
PLScanView *scanView = [[PLScanView alloc] initScanView];
scanView.frame = CGRectMake(0, 64, SCREENWIDTH, SCREENHEIGHT - 64);
[self.view addSubview:scanView];
self.scanView = scanView;
在自定的PLScanView(继承自UIView)的文件中,写了一个其他文件可以调用的实例方法:
- (instancetype)initScanView;
在这个方法的实现中,添加必要的视图 并且

视图背景色必须设置为clearColor,是为了可以看到摄像头照射的内容。
1、添加视图
   // 1、添加描述信息
    UILabel *messageLbl = [[UILabel alloc] init];
    messageLbl.text = @"请将条码置于取景器内进行扫描,距离大约10cm,尽量避免反光,开启闪光灯等有助于弱光条件下识别";
    messageLbl.numberOfLines = 0;
    messageLbl.font = [UIFont systemFontOfSize:12.0];
    messageLbl.textColor = [UIColor whiteColor];
    [self addSubview:messageLbl];
    self.messageLbl = messageLbl;
    
    // 2、添加imageView与滚动线
    // 2.1 设置中间扫描区域的背景图片
    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"pick_bg.png"]];
    //添加到视图上
    [self addSubview:imageView];
    self.imageView = imageView;
    
    // 2.2 初始化二维码的扫描线
    UIImageView *lineImageView = [[UIImageView alloc] init];
    lineImageView.image = [UIImage imageNamed:@"line.png"];
    [imageView addSubview:lineImageView];
    self.lineImageView = lineImageView;
    
    //开启定时器
    [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(animation) userInfo:nil repeats:YES];
2、在layoutSubviews中设置布局--设置矩形宽250 高150:
  - (void)layoutSubviews
 {
     [super layoutSubviews];
    //描述的label
    self.messageLbl.frame = CGRectMake(10, 20, self.width - 20, 30);
    //设置位置到界面的中间
    self.imageView.frame = CGRectMake(self.bounds.size.width * 0.5 - 250 * 0.5, self.bounds.size.height * 0.5 - 150 * 0.5, 250, 150);
  // 开始位置
    self.lineImageView.frame  = CGRectMake(10, 10, 230, 2);
}
3、定时器的实现:
   - (void)animation
{
     [UIView animateWithDuration:1.8 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
    // 最低位置
    self.lineImageView.frame = CGRectMake(10, 130, 230, 2);
    
     } completion:^(BOOL finished) {
    // 开始位置
        self.lineImageView.frame = CGRectMake(10, 10, 230, 2);
    }];
}
4、中间清晰 周围模糊的界面实现,在drawRect中绘制,代码如下:
// 1 获得图形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 2 画整个屏幕的视图颜色
CGContextSetRGBFillColor(ctx, 40/255.0, 40/255.0, 40/255.0, 0.5);
CGContextFillRect(ctx, CGRectMake(0, 0, self.width, self.height));

// 3 画中间透明视图 clearRect
CGRect clearRect = CGRectMake((self.width - 250) * 0.5, (self.height - 150) * 0.5, 250, 150);
CGContextClearRect(ctx, clearRect);
5、在控制器的viewWillAppear中,如果该控制器被pop之后,还要再次可以扫描,那么AVFoundation的5个变量,要依然存在,所以只需要重新调用上文“一”的代码即可:
  if (!self.session.running) {
      // 调用代码一...
   }
6、只能在中间区域内扫描出功能实现rectOfInterest属性 -- 修正扫描区域 右上角为坐标原点,但是传入的值依然按右上角坐标原点的Y/X/H/W进行理解(右上角为原点下的YXHW)
CGFloat scaleY = (SCREENHEIGHT - 150) * 0.5 / SCREENHEIGHT;
CGFloat scaleX = (SCREENWIDTH - 250) * 0.5 / SCREENWIDTH;
CGFloat scaleH = 150 / SCREENHEIGHT;
CGFloat scaleW = 250 / SCREENWIDTH;
self.output.rectOfInterest = CGRectMake(scaleY, scaleX, scaleH, scaleW);
注:关于self.output.rectOfInterest方法,使用解析如下

//这个CGRectMake(Y,X,H,W) 1代表最大值 原点是导航右上角 为起始点
[output setRectOfInterest:CGRectMake(0, 0.5, 0.5, 0.5)];//左上角 1/4 屏幕
[output setRectOfInterest:CGRectMake(0.5, 0.5, 0.5, 0.5)];//左下角 1/4 屏幕
[output setRectOfInterest:CGRectMake(0.5, 0, 0.5, 0.5)]; //右下角 1/4 屏幕
[output setRectOfInterest:CGRectMake(0, 0, 0.5, 0.5)]; //右上角 1/4 屏幕
[output setRectOfInterest:CGRectMake(0.25,0.25, 0.5, 0.5)]; //貌似 中间的感觉!!!

你可能感兴趣的:(条形码扫描)