这是之前做的一个项目,最近在忙完新的项目之后突发奇想总结一下之前遇到的坑吧,今天只说利用苹果原生框架的二维码扫描,原生的二维码扫描获取信息比较快捷,延迟很短,所以用户的体验会很好,当然,现在网上各种第三方封装的也是很好的,在这里只是做一个适应性二维码的简单说明,好了,废话一连篇,下面直接上代码:
再废话一下,苹果自带的二维码扫描死定义在AVFoundation框架里面,通过在这个框架里面设置好相机的输入,输出资源,以及设置好相应的会话就可以,其实很简单
首先需要先定义好几个属性 ,媒体设备 输入 输出 会话
- (AVCaptureDevice *)device
{
if (_device == nil) {
_device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
}
return _device;
}
- (AVCaptureDeviceInput *)input
{
if (_input == nil) {
_input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil];
}
return _input;
}
- (AVCaptureSession *)session
{
if (_session == nil) {
_session = [[AVCaptureSession alloc] init];
}
return _session;
}
- (AVCaptureVideoPreviewLayer *)previewLayer
{
if (_previewLayer == nil) {
_previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
}
return _previewLayer;
}
这里需要设置一下扫描的区域大小,设置相机的感兴趣范围大小
、、、
// 设置输出对象解析数据时感兴趣的范围
// 默认值是 CGRect(x: 0, y: 0, width: 1, height: 1)
// 通过对这个值的观察, 我们发现传入的是比例
// 注意: 参照是以横屏的左上角作为, 而不是以竖屏
// out.rectOfInterest = CGRect(x: 0, y: 0, width: 0.5, height: 0.5)
- (AVCaptureMetadataOutput *)output
{
if (_output == nil) {
_output = [[AVCaptureMetadataOutput alloc] init];
// 1.获取屏幕的frame
CGRect viewRect = self.view.frame;
// 2.获取扫描容器的frame
CGRect containerRect = self.customContainerView.frame;
CGFloat x = containerRect.origin.y / viewRect.size.height;
CGFloat y = containerRect.origin.x / viewRect.size.width;
CGFloat width = containerRect.size.height / viewRect.size.height;
CGFloat height = containerRect.size.width / viewRect.size.width;
// CGRect outRect = CGRectMake(x, y, width, height);
// [_output rectForMetadataOutputRectOfInterest:outRect];
_output.rectOfInterest = CGRectMake(x, y, width, height);
}
return _output;
}
、、、
这里谁做边界的描边,对UI做的处理
、、、
- (CALayer *)containerLayer
{
if (_containerLayer == nil) {
_containerLayer = [[CALayer alloc] init];
}
return _containerLayer;
}
、、、
主要的代码功能在这里:
、、、
- (void)startScan
{
// 1.判断输入能否添加到会话中
if (![self.session canAddInput:self.input]) return;
[self.session addInput:self.input];
// 2.判断输出能够添加到会话中
if (![self.session canAddOutput:self.output]) return;
[self.session addOutput:self.output];
// 4.设置输出能够解析的数据类型
// 注意点: 设置数据类型一定要在输出对象添加到会话之后才能设置
self.output.metadataObjectTypes = self.output.availableMetadataObjectTypes;
// 5.设置监听监听输出解析到的数据
[self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
// 6.添加预览图层
[self.view.layer insertSublayer:self.previewLayer atIndex:0];
self.previewLayer.frame = self.view.bounds;
// self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
// 7.添加容器图层
[self.view.layer addSublayer:self.containerLayer];
self.containerLayer.frame = self.view.bounds;
// 8.开始扫描
[self.session startRunning];
}
、、、
扫描成功之后的回调走这里,这里我们公司做的是到店付款页面的额跳转,在这里可以根据需求进行修改
、、、
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
// if (metadataObjects.count > 0) {
// id 类型不能点语法,所以要先去取出数组中对象
AVMetadataMachineReadableCodeObject *object = [metadataObjects lastObject];
if (object == nil) {
return;
} else {
// 只要扫描到结果就会调用
// self.customLabel.text = object.stringValue;
[self clearLayers];
// [self.previewLayer removeFromSuperlayer];
// 2.对扫描到的二维码进行描边
AVMetadataMachineReadableCodeObject *obj = (AVMetadataMachineReadableCodeObject *)[self.previewLayer transformedMetadataObjectForMetadataObject:object];
[self drawLine:obj];
// 去买单
self.hidesBottomBarWhenPushed = YES;
UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
KZMWriteCountViewController *toPayVC = [storyBoard instantiateViewControllerWithIdentifier:@"writeMoneyCountVCV"];
toPayVC.objectValue = object.stringValue;
// 停止扫描
[self.session stopRunning];
[self.navigationController pushViewController:toPayVC animated:YES];
//
// // 将预览图层移除
// [self.previewLayer removeFromSuperlayer];
// } else {
// NSLog(@"没有扫描到数据");
// }
}
}
、、、
在扫描成功只有可以对二维码进行描边
、、、
// 绘制描边
- (void)drawLine:(AVMetadataMachineReadableCodeObject *)objc
{
NSArray *array = objc.corners;
// 1.创建形状图层, 用于保存绘制的矩形
CAShapeLayer *layer = [[CAShapeLayer alloc] init];
// 设置线宽
layer.lineWidth = 2;
layer.strokeColor = [UIColor greenColor].CGColor;
layer.fillColor = [UIColor clearColor].CGColor;
// 2.创建UIBezierPath, 绘制矩形
UIBezierPath *path = [[UIBezierPath alloc] init];
CGPoint point = CGPointZero;
int index = 0;
CFDictionaryRef dict = (__bridge CFDictionaryRef)(array[index++]);
// 把点转换为不可变字典
// 把字典转换为点,存在point里,成功返回true 其他false
CGPointMakeWithDictionaryRepresentation(dict, &point);
[path moveToPoint:point];
// 2.2连接其它线段
for (int i = 1; i<array.count; i++) {
CGPointMakeWithDictionaryRepresentation((__bridge CFDictionaryRef)array[i], &point);
[path addLineToPoint:point];
}
// 2.3关闭路径
[path closePath];
layer.path = path.CGPath;
// 3.将用于保存矩形的图层添加到界面上
[self.containerLayer addSublayer:layer];
}
、、、
最后需要清除layer图层,代码里的注释都很清晰了,这里就不再多做赘述了,
、、、
- (void)clearLayers
{
if (self.containerLayer.sublayers)
{
for (CALayer *subLayer in self.containerLayer.sublayers)
{
[subLayer removeFromSuperlayer];
}
}
}
、、、
到这里就剩下一个采集的动画了,这个采集的动画很简单,可以跟你们UI要几张连续的图片然后利用UIView的基本动画连贯起来,也可以使用CoreAnimation框架来做,我这里是利用CoreAnimation做的,比较简单
、、、
// 开启冲击波动画
- (void)startAnimation
{
// 1.设置冲击波底部和容器视图顶部对齐
// self.scanLineTopConstraint.constant = - self.containerHeightConstraint.constant;
[self.scanLineImageView setFrame:CGRectMake(0, 0, 300, 0)];
// 刷新UI
[self.view layoutIfNeeded];
// 2.执行扫描动画
[UIView animateWithDuration:1.5 animations:^{
// 无线重复动画
[UIView setAnimationRepeatCount:MAXFLOAT];
[self.scanLineImageView setFrame:CGRectMake(0, 0, 300, 300)];
// self.scanLineTopConstraint.constant = self.containerHeightConstraint.constant;
// [self.scanLineImageView setFrame:CGRectMake(0, 0, 300, 0)];
// 刷新UI
[self.view layoutIfNeeded];
} completion:nil];
}
、、、
好了,iOS苹果原生的二维码扫描到这就结束了,直接复制粘贴代码就行,只需要根据你自己的工程项目稍作修改就可以了,有不足之处,还望各位大神指正。