条形码扫描

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

界面展示.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)]; //貌似 中间的感觉!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容