在iOS上使用原生框架实现扫码功能
最编程
2024-02-13 19:15:14
...
利用系统自带框架实现扫一扫功能
实现功能前的项目配置
因为该项目要使用到相机和相册。所以我们要在info.plist中设置询问用户是否允许访问的权限。因为需要调用摄像头,所以要在真机上运行(在模拟器运行会崩溃)。
功能分析
从功能需求分析来看,扫一扫该功能可以分为以下几个功能点:
- 在启动设备时设置loading view
- 使用CGContextRef绘制扫一扫界面UI
- 使用NSTimer实现扫描线动画
- 使用AVFoundation框架实现扫描功能
- 实现扫描二维码图片(系统只支持二维码,不支持条形码),调用系统闪光灯
- 在扫描完成后将值传给上一个界面(Block反向传值)
具体实现
- 在启动设备时设置loading view
1.创建继承UIActivityIndicatorView
的LoadView,在.m文件中写初始化代码:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// 菊花背景的大小
self.frame = CGRectMake((ScreenWidth - 100)/2, (ScreenHeight - 100)/2, 100, 100);
// 菊花的背景色
self.backgroundColor = [UIColor blackColor];
self.layer.cornerRadius = 10;
// 菊花的颜色和格式(白色、白色大、灰色)
self.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
// 在菊花下面添加文字
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(10, 60, 80, 40)];
label.text = @"loading...";
label.font = [UIFont systemFontOfSize:14];
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor whiteColor];
[self addSubview:label];
}
return self;
}
2.将LoadView添加到bgView中:
- (void)setupBgView {
_bgView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight)];
_bgView.backgroundColor = [UIColor blackColor];
LoadView *loadView = [[LoadView alloc]init];
[_bgView addSubview:loadView];
// 动画开始
[loadView startAnimating];
}
- 使用CGContextRef绘制扫一扫界面UI
1.创建继承与UIView的ScanView,在.m文件中写下面的绘制代码:
- (void)drawRect:(CGRect)rect {
CGFloat rectWidth = 50;
CGFloat rectHeight = 200;
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat black[4] = {0.0, 0.0, 0.0, _alphaValue};
CGContextSetFillColor(context, black);
//top
CGRect rect1 = CGRectMake(0, 0, self.frame.size.width, rectHeight);
CGContextFillRect(context, rect1);
//left
rect1 = CGRectMake(0, rectHeight, rectWidth, rectHeight);
CGContextFillRect(context, rect1);
//bottom
rect1 = CGRectMake(0, rectHeight * 2, self.frame.size.width, self.frame.size.height - rectHeight * 2);
CGContextFillRect(context, rect1);
//right
rect1 = CGRectMake(self.frame.size.width - rectWidth, rectHeight, rectWidth, rectHeight);
CGContextFillRect(context, rect1);
CGContextStrokePath(context);
//中间画矩形(正方形)
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextSetLineWidth(context, 1);
CGContextAddRect(context, CGRectMake(rectWidth, rectHeight, self.frame.size.width - rectWidth * 2, rectHeight));
CGContextStrokePath(context);
CGFloat lineWidth = 10;
CGContextSetStrokeColorWithColor(context, [UIColor greenColor].CGColor);
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
// Draw them with a 2.0 stroke width so they are a bit more visible.
CGContextSetLineWidth(context, 2.0);
//左上角水平线
CGContextMoveToPoint(context, rectWidth, rectHeight);
CGContextAddLineToPoint(context, rectWidth + lineWidth, rectHeight);
//左上角垂直线
CGContextMoveToPoint(context, rectWidth, rectHeight);
CGContextAddLineToPoint(context, rectWidth, rectHeight + lineWidth);
//左下角水平线
CGContextMoveToPoint(context, rectWidth, rectHeight * 2);
CGContextAddLineToPoint(context, rectWidth + lineWidth, rectHeight * 2);
//左下角垂直线
CGContextMoveToPoint(context, rectWidth, rectHeight * 2 - lineWidth);
CGContextAddLineToPoint(context, rectWidth, rectHeight * 2);
//右上角水平线
CGContextMoveToPoint(context, self.frame.size.width - rectWidth - lineWidth, rectHeight);
CGContextAddLineToPoint(context, self.frame.size.width - rectWidth, rectHeight);
//右上角垂直线
CGContextMoveToPoint(context, self.frame.size.width - rectWidth, rectHeight);
CGContextAddLineToPoint(context, self.frame.size.width - rectWidth, rectHeight + lineWidth);
//右下角水平线
CGContextMoveToPoint(context, self.frame.size.width - rectWidth - lineWidth, rectHeight * 2);
CGContextAddLineToPoint(context, self.frame.size.width - rectWidth, rectHeight * 2);
//右下角垂直线
CGContextMoveToPoint(context, self.frame.size.width - rectWidth, rectHeight * 2 - lineWidth);
CGContextAddLineToPoint(context, self.frame.size.width - rectWidth, rectHeight * 2);
CGContextStrokePath(context);
}
2.将scanView添加到self.view中:
- (void)setupScanView {
_scan = [[ScanView alloc]initWithFrame:self.view.bounds];
_scan.backgroundColor = [UIColor clearColor];
_slideLineView = [[UIView alloc]initWithFrame:CGRectMake(_viewWidth, 201, ScreenWidth - _viewWidth * 2, 1)];
_slideLineView.backgroundColor = [UIColor greenColor];
[_scan addSubview:_slideLineView];
[self.view addSubview:_scan];
[self setupSubView];
}
3.设置self.view中的闪光灯按钮和访问相册按钮:
- (void)setupSubView {
_titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 500, ScreenWidth, 50.0)];
_titleLabel.text = @"请将二维码放入框内";
_titleLabel.textAlignment = NSTextAlignmentCenter;
_titleLabel.textColor = [UIColor whiteColor];
[_scan addSubview:_titleLabel];
_lightButton = [[UIButton alloc]initWithFrame:CGRectMake(100, 580, 50, 50)];
[_lightButton setTitle:@"light" forState:UIControlStateNormal];
[_lightButton addTarget:self action:@selector(lightButtonDidTouch) forControlEvents:UIControlEventTouchUpInside];
[_scan addSubview:_lightButton];
_imageButton = [[UIButton alloc]initWithFrame:CGRectMake(200, 580, 50, 50)];
[_imageButton setTitle:@"相册" forState:UIControlStateNormal];
[_imageButton addTarget:self action:@selector(imageButtonDidTouch) forControlEvents:UIControlEventTouchUpInside];
[_scan addSubview:_imageButton];
}
4.闪光灯按钮的点击事件:
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if (![device hasTorch]) {
NSLog(@"no torch");
}else {
[device lockForConfiguration:nil];
if (!self.isOpen) {
[device setTorchMode: AVCaptureTorchModeOn];
self.isOpen = YES;
}
else {
[device setTorchMode: AVCaptureTorchModeOff];
self.isOpen = NO;
}
[device unlockForConfiguration];
}
5.访问相册按钮的点击事件:
- (void)imageButtonDidTouch {
[_timer invalidate];
_timer = nil;
UIImagePickerController *picker = [[UIImagePickerController alloc]init];
//设置图片源(相簿)
picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
//设置代理
picker.delegate = self;
//设置可以编辑
picker.allowsEditing = YES;
//打开拾取器界面
[self presentViewController:picker animated:YES completion:nil];
}
#pragma mark UIImagePickerControllerDelegate methods
//完成选择图片
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo {
// 销毁控制器
[picker dismissViewControllerAnimated:YES completion:nil];
// 根据URL找到CIImage
CIImage *ciImage = [[CIImage alloc]initWithCGImage:image.CGImage];
if (ciImage){
// 创建CIDetector
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{ CIDetectorAccuracy: CIDetectorAccuracyHigh }];
NSArray *features = [detector featuresInImage:ciImage];
if ([features count] > 0) {
for (CIFeature *feature in features) {
if (![feature isKindOfClass:[CIQRCodeFeature class]]) {
continue;
}
CIQRCodeFeature *qrFeature = (CIQRCodeFeature *)feature;
NSString *code = qrFeature.messageString;
if (self.resultBlock) {
self.resultBlock(code);
[self scanSuccess];
}
//输出扫描字符串
[self.navigationController popViewControllerAnimated:YES];
}
}else {
[self setupTimer];
}
}
}
//取消选择图片
-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[picker dismissViewControllerAnimated:YES completion:nil];
}
- 使用NSTimer实现扫描线动画
实现扫描线代码如下:
- (void)setupTimer {
_timer = [NSTimer scheduledTimerWithTimeInterval:1.8 target:self selector:@selector(animationView) userInfo:nil repeats:YES];
[_timer fire];
}
- (void)animationView {
[UIView animateWithDuration:1.5 animations:^{
_slideLineView.transform = CGAffineTransformMakeTranslation(0, 200);
} completion:^(BOOL finished) {
_slideLineView.transform = CGAffineTransformIdentity;
}];
}
- 使用AVFoundation实现扫描功能
1.导入,遵守AVCaptureMetadataOutputObjectsDelegate。
初始化代码如下:
- (void)setupAVFoundation {
//获取摄像设备
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
//创建输入流
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
//创建输出流
AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc]init];
//设置代理 在主线程里刷新
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
//初始化链接对象
_session = [[AVCaptureSession alloc]init];
//高质量采集率
[_session setSessionPreset:AVCaptureSessionPresetHigh];
[_session addInput:input];
[_session addOutput:output];
//设置扫码支持的编码格式(如下设置条形码和二维码兼容)
output.metadataObjectTypes=@[AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code];
_previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_session];
_previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
_previewLayer.frame = self.view.layer.bounds;
[self.view.layer insertSublayer:_previewLayer atIndex:0];
//开始捕获
[_session startRunning];
//移除loading view
[_bgView removeFromSuperview];
}
2.实现AVCaptureMetadataOutputObjectsDelegate
#pragma mark 输出的代理
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
if (metadataObjects.count > 0) {
[_timer invalidate];
_timer = nil;
[_session stopRunning];
AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects objectAtIndex: 0];
if (self.resultBlock) {
self.resultBlock(metadataObject.stringValue);
[self scanSuccess];
}
//输出扫描字符串
[self.navigationController popViewControllerAnimated:YES];
}
}
//扫描成功的提示音
- (void)scanSuccess {
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
AudioServicesPlaySystemSound(1109);
}
结束语
至此,即可实现利用原生框架扫描二维码的功能,使用原生有一个缺陷就是无法扫描图片中的条形码。如要实现这个功能可以使用 ZXingObjC
框架。
完整项目地址,第十个
参考链接
- iOS 二维码扫描
- iOS解码二维码图片
- iOS 原生二维码扫描(可限制扫描区域)
- iOS7使用原生API进行二维码和条形码的扫描
- ios8原生库实现扫描相册内二维码图片
- iOS调用闪光灯的代码
- iOS开发中在加载页面添加菊花动画(非第三方
- 苹果开发 笔记(48) UIImage CIImage CGImageRef
- iOS 原生扫 QR 码的那些事
- CGContextRef使用简要教程
上一篇: 如何在你的环境中安装 Lua?
下一篇: lua self this
推荐阅读
-
微信 "扫一扫 "物联网,全面揭秘 "扫一扫 "背后的扫盲技术!-1.1 扫一扫感知物体是做什么的? 1.1 微信扫一扫是做什么的? 扫一扫识物是指以图片或视频(商品图片:鞋/包/美妆/服饰/家电/玩具/图书/食品/珠宝/家具/其他商品)为输入媒介,挖掘微信内容生态中的有价值信息(电商+百科+资讯,如图1所示),并展示给用户。这里的电商基本涵盖了微信小程序覆盖上亿SKU的全量优质电商,可以支持用户货比N家并直接下单购买,百科和资讯则聚合了微信内的头部自媒体如搜狗、搜搜、百度等,向用户展示和分享拍摄商品相关的内容资讯。 图 1 扫一扫识别功能示意图 欢迎大家更新iOS新版微信→扫一扫→识货,亲自体验,也欢迎大家通过识货界面的反馈按钮向我们提交反馈意见。 扫一扫识物实景图展示 1.2 扫一扫识物有哪些使用场景? 扫一扫识物的目的是为用户访问微信内部生态内容开辟一个新窗口,以用户扫图片为输入形式,为用户提供微信生态内容中的百科、资讯、电商等作为展示页面。除了用户熟悉的扫一扫操作外,我们还将进一步拓展长按操作,让用户更方便地进行扫一扫操作。"扫一扫知事 "的落地场景主要涵盖三大部分: a. 科普知识: a.科普知识。用户通过扫一扫,可以在微信生态圈中获取该对象的百科、资讯等常识或趣闻,帮助用户更好地了解该对象; b.购物场景。同样的搜索功能支持用户看到喜欢的商品立即检索到微信小程序电商中的同款商品,支持用户即扫即购; c.广告场景。扫一扫识别物体可以辅助公众号文章、视频更好地理解其中蕴含的图片信息,从而更好地投放匹配广告,提高点击率。 1.3 Sweep Sense 为 Sweep 家族带来了哪些新技术? 对于扫一扫来说,大家耳熟能详的应该就是扫一扫二维码、扫一扫小程序码、扫一扫条形码、扫一扫翻译了。无论是各种形式的编码还是文字字符,都可以看作是图片的一种特定编码形式,而物的识别则是对自然场景图片的识别,这对于扫一扫家族来说是一个质的飞跃,我们希望从物的识别入手,进一步拓展扫一扫对自然场景图片的理解能力,比如扫酒、扫车、扫植物、扫人脸等服务,如下图3所示。 图 3 Sweep 家族
-
Adobe国际认证中文官方网站】Adobe中国摄影计划,免费安装正版激活--Adobe Creative Cloud中国摄影计划。与此同时,Adobe宣布天猫为Adobe Creative Cloud中国摄影计划的电商战略合作伙伴,并将与其合作上线Adobe天猫官方旗舰店。 此举无疑一方面扩大了Adobe在中国的影响力,另一方面也有助于国内用户更好地培养正版软件意识,推动Adobe软件在中国的正版化进程。 网络异常,图片无法显示 ||网络异常 Adobe Creative Cloud中国摄影计划包括Photoshop和Lightroom Classic两大桌面创意工具,以及iOS版Photoshop Express。 其中,Adobe Lightroom Classic和Adobe Photoshop作为两款常用的图像处理软件,对于那些玩摄影、后期修图的创意设计人群无疑有着巨大的帮助,而LR+PS套装对于摄影领域用户的重要性自不必说,正版产品的性能实时更新也可以放心!体验最新功能,对于新镜头(补偿)和机身(RAW 读取)都能第一时间适应。不信你看: Photoshop 图像合成 裁剪、移除对象、润饰合成照片、玩转色彩和特效,创建精美图片和艺术品! Lightroom Classic 照片编辑 轻松批量管理和编辑照片,内置专业创意控件和摄影师预设,让你的照片大放异彩。 手机 PS 便捷编辑 Photoshop Express 支持多种滤镜、贴纸,手机即可完成抠图、除雾等任务 人工智能编辑工具 神经滤镜、快速点击选区、自动选择主题等人工智能功能让图像编辑更轻松 创意画笔内容识别 定制艺术画笔工具,实现个性化效果;内容识别填充,智能去除无用物体。 Adobe Creative Cloud 中国摄影计划的推出,为中国的专业摄影师、摄影爱好者、后期修图和其他创意设计人员带来了全方位的内容和体验。 网络异常,图片无法显示 ||网络异常 当然,不可否认的是,"由于盗版软件缺乏开发、维护和升级成本,销售价格远低于正版软件。再加上很多普通人并不需要使用正版软件的复杂功能,版权观念较淡,还是有大量的创意设计人员会选择盗版软件"。 但事实上,当所有的软件都不再是单一的软件,而是变成一种服务时,单机版盗版的存在就逐渐成为鸡肋。因为有太多的服务让你即使是所谓的 "完美破解",也无法享受,Adobe Cloud 就是一个很好的例子,所谓的完美破解,你只能使用 "Adobe "的一半,对于更精彩的 "云",只能望云兴叹。更何况,越来越多的设计工具从免费走向付费,越来越多的设计师和企业已经接受了付费使用的模式。 其次,对于互联网时代的企业数字化转型而言,数字化合规至关重要。21年来,使用盗版PS和未经授权的方正字体被指侵权的事情闹得沸沸扬扬,虽然新闻真假难辨,但也给使用盗版工具的用户敲响了警钟。 付费使用正版工具,可以更放心地进行设计,不用担心版权风险!
-
在同一台电脑或手机的不同浏览器上使用微信扫码实现无间断登录的方法
-
使用Vue在PC端实现扫码快速登录功能
-
在iOS上使用原生框架实现扫码功能