使用CAShapeLayer实现扫描二维码界面

来源:互联网 发布:java常用的分布式框架 编辑:程序博客网 时间:2024/05/29 14:45

  • CAShapeLayer简介
  • 实现扫描界面
  • awakeFromNib代码说明
  • ROI区域的说明
  • 效果展示

之前写过一篇利用AVCaptureSession扫描二维码的博客,但是那个页面是全屏的,在实际应用中,一般都是中间有一个框,然后二维码对准中间框的时候才能识别。这是怎么实现的呢,经过查找资料,现将方法纪录,供日后查阅。

CAShapeLayer简介

关于CAShapeLayer的介绍网上有很多,我就引用一段吧。原文链接

CAShapeLayer是一个通过矢量图形而不是bitmap来绘制的图层子类。你指定诸如颜色和线宽等属性,用CGPath来定义想要绘制的图形,最后CAShapeLayer就自动渲染出来了。当然,你也可以用Core Graphics直接向原始的CALyer的内容中绘制一个路径,相比直下,使用CAShapeLayer有以下一些优点:

  • 渲染快速。CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多。
  • 高效使用内存。一个CAShapeLayer不需要像普通CALayer一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存
  • 不会被图层边界剪裁掉。一个CAShapeLayer可以在边界之外绘制。你的图层路径不会像在使用Core Graphics的普通CALayer一样被剪裁掉
  • 不会出现像素化。当你给CAShapeLayer做3D变换时,它不像一个有寄宿图的普通图层一样变得像素化

实现扫描界面

首先当然是需要配备一张有扫描框的图片:
扫描框
我们定义一个ScanView,用来展示整个扫描界面,使用XIB或其他合适方式将图片添加到该View中,我这里使用XIB方式,并设置好约束。

@interface CMQRCodeScanView : UIView {    __weak IBOutlet UIImageView *scanImageView;}@end

首先定义两个与屏幕相关的宏,并定义图片的大小(扫描区域的大小),一般来说要比图片大小要稍小一点,不然看起来效果有点差强人意,这里我图片设置的大小是240。

#define UI_IOS_WINDOW_WIDTH  CGRectGetWidth([UIScreen mainScreen].bounds)#define UI_IOS_WINDOW_HEIGHT CGRectGetHeight([UIScreen mainScreen].bounds)#define kScanRegionSize 232

awakeFromNib代码说明

定义一个CAShapeLayer类变量,并在-awakeFromNib方法中编写相关代码

-(void)awakeFromNib {    [super awakeFromNib];    CGMutablePathRef path = CGPathCreateMutable();    CGPathAddRect(path, NULL, CGRectMake(0, 0, UI_IOS_WINDOW_WIDTH, UI_IOS_WINDOW_HEIGHT));    CGPathAddRect(path, NULL, CGRectMake((UI_IOS_WINDOW_WIDTH - kScanRegionSize) / 2, (UI_IOS_WINDOW_HEIGHT - kScanRegionSize) / 2, kScanRegionSize, kScanRegionSize));    _shapeLayer = [[CAShapeLayer alloc] init];    _shapeLayer.fillColor = [UIColor colorWithWhite:0.0f alpha:0.7f].CGColor;    _shapeLayer.fillRule = kCAFillRuleEvenOdd;    _shapeLayer.path = path;    [self.layer insertSublayer:_shapeLayer atIndex:0];    return ;}

因为除了中间的扫描区域之外,其他的全部要置灰,所以要添加两条路径,一条是屏幕大小,还有一条是扫描区域大小。然后将路径赋给CAShapeLayer的path属性。

CGMutablePathRef path = CGPathCreateMutable();    CGPathAddRect(path, NULL, CGRectMake(0, 0, UI_IOS_WINDOW_WIDTH, UI_IOS_WINDOW_HEIGHT));    CGPathAddRect(path, NULL, CGRectMake((UI_IOS_WINDOW_WIDTH - kScanRegionSize) / 2, (UI_IOS_WINDOW_HEIGHT - kScanRegionSize) / 2, kScanRegionSize, kScanRegionSize));_shapeLayer.path = path;

然后设置其他区域的相关属性,如填充颜色、填充规则。主要是填充规则,需要设置为kCAFillRuleEvenOdd,关于这个属性的介绍也有很多文章介绍(其实我自己也不太明白,不过我的目的是实现)

_shapeLayer.fillColor = [UIColor colorWithWhite:0.0f alpha:0.7f].CGColor;_shapeLayer.fillRule = kCAFillRuleEvenOdd;

最后将该CAShapeLayer插入到最底层即可实现。如果需要显示摄像头的拍摄页面,只需要将AVCaptureVideoPreviewLayer预览层插入到CAShapeLayer之下,并设置ROI区域为扫描框区域即可。

ROI区域的说明

虽然知道要设置ROI区域,但是当我自己设置的时候还是会出了点问题,刚开始,我设置的是扫描框的矩形区域,但是发现扫描不到二维码,于是查看文档得知道,ROI区域的默认值是(0.0, 0.0, 1.0, 1.0),原来ROI区域用的是比例,所以设置代码应该如下:

[outPut setRectOfInterest:CGRectMake((UI_IOS_WINDOW_WIDTH - kScanRegionSize) / (2 * UI_IOS_WINDOW_WIDTH), (UI_IOS_WINDOW_HEIGHT - kScanRegionSize) / (2 * UI_IOS_WINDOW_HEIGHT), (UI_IOS_WINDOW_WIDTH + kScanRegionSize) / (2 * UI_IOS_WINDOW_WIDTH), (kScanRegionSize + UI_IOS_WINDOW_HEIGHT) / (2 *UI_IOS_WINDOW_HEIGHT))];

效果展示

Demo

0 0
原创粉丝点击