iOS功能 - 扫描二维码、条形码
扫描二维码 | 条形码
1 - 准备工作
① 配置 Info.plist 文件(相机权限)
<key>NSCameraUsageDescription</key> <string>App需您的同意才能访问相机</string>
② 使用到的扩展:充当遮罩层
// - UIImage+mask.h
1 #import <UIKit/UIKit.h> 2 @interface UIImage (mask) 3 /** 4 * @param maskRect 遮罩层的 Rect 5 * @param clearRect 镂空的 Rect 6 * 7 * @return 遮罩层图片 8 */ 9 + (UIImage *)maskImageWithMaskRect:(CGRect)maskRect clearRect:(CGRect)clearRect; 10 11 @end
// - UIImage+mask.m
1 #import "UIImage+mask.h" 2 @implementation UIImage (mask) 3 4 + (UIImage *)maskImageWithMaskRect:(CGRect)maskRect clearRect:(CGRect)clearRect{ 5 6 UIGraphicsBeginImageContext(maskRect.size); 7 CGContextRef ctx = UIGraphicsGetCurrentContext(); 8 CGContextSetRGBFillColor(ctx, 0,0,0,0.6); 9 CGRect drawRect =maskRect; 10 11 CGContextFillRect(ctx, drawRect); 12 13 drawRect = clearRect; 14 CGContextClearRect(ctx, drawRect); 15 16 UIImage* returnimage = UIGraphicsGetImageFromCurrentImageContext(); 17 UIGraphicsEndImageContext(); 18 return returnimage; 19 } 20 21 @end
2 - 代码示例:实现二维码扫描、手电筒功开关功能
// - ScanQRcodeVC.h:扫码页面
1 #import <UIKit/UIKit.h> 2 #import <AVFoundation/AVFoundation.h> 3 @interface ScanQRcodeVC : UIViewController<AVCaptureMetadataOutputObjectsDelegate>{ 4 5 int num; 6 BOOL upOrdown; 7 NSTimer *timer; 8 } 9 10 @property (strong,nonatomic)AVCaptureDevice * device; 11 @property (strong,nonatomic)AVCaptureDeviceInput * input; 12 @property (strong,nonatomic)AVCaptureMetadataOutput * output; 13 @property (strong,nonatomic)AVCaptureSession * session; 14 @property (strong,nonatomic)AVCaptureVideoPreviewLayer * preview; 15 @property (nonatomic, retain) UIImageView * line; 16 17 @end
// - ScanQRcodeVC.m
1 #define MainScreen_W [UIScreen mainScreen].bounds.size.width 2 #define MainScreen_H [UIScreen mainScreen].bounds.size.height 3 #define MiddleWidth 0.72*MainScreen_W // 扫码框宽度 4 #define Top_Height 0.2*MainScreen_H // 扫码框距顶部距离 5 #import "ScanQRcodeVC.h" 6 #import "UIImage+mask.h" 7 @interface ScanQRcodeVC (){ 8 bool _canOpen; 9 } 10 @end 11 12 @implementation ScanQRcodeVC 13 14 - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{ 15 self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 16 if (self) { 17 _canOpen = NO; 18 } 19 return self; 20 } 21 22 23 - (void)viewDidLoad { 24 [super viewDidLoad]; 25 [self creatUI];// 创建 UI 26 [self setupCamera]; 27 28 } 29 30 - (void)didReceiveMemoryWarning { 31 [super didReceiveMemoryWarning]; 32 // Dispose of any resources that can be recreated. 33 } 34 35 - (void)setupCamera{ 36 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 37 38 if (!_device) { 39 _device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 40 // Input 41 _input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil]; 42 43 // Output 44 _output = [[AVCaptureMetadataOutput alloc]init]; 45 [_output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; 46 47 // Session 48 _session = [[AVCaptureSession alloc]init]; 49 [_session setSessionPreset:AVCaptureSessionPresetHigh]; 50 if ([_session canAddInput:self.input]){ 51 [_session addInput:self.input]; 52 _canOpen = YES; 53 }else{ 54 dispatch_async(dispatch_get_main_queue(), ^{ 55 // 回到主线程 56 UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"相机权限受限" message:@"请进入设置选项,点击此APP进行授权操作" preferredStyle:UIAlertControllerStyleAlert]; 57 UIAlertAction *OKactuon = [UIAlertAction actionWithTitle:@"好" style:UIAlertActionStyleDefault handler:nil]; 58 [alertVC addAction:OKactuon]; 59 [self presentViewController:alertVC animated:YES completion:nil]; 60 }); 61 } 62 63 if (_canOpen) { 64 if ([_session canAddOutput:self.output]){ 65 [_session addOutput:self.output]; 66 } 67 // 条形码/二维码 68 _output.metadataObjectTypes =[NSArray arrayWithObjects:AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code, AVMetadataObjectTypeQRCode, nil]; 69 70 // 只支持二维码 71 //_output.metadataObjectTypes =@[AVMetadataObjectTypeQRCode]; 72 73 _preview =[AVCaptureVideoPreviewLayer layerWithSession:self.session]; 74 _preview.videoGravity = AVLayerVideoGravityResizeAspectFill; 75 dispatch_async(dispatch_get_main_queue(), ^{ 76 // 回到主线程 77 _preview.frame =CGRectMake(0,0,MainScreen_W,MainScreen_H); 78 [self.view.layer insertSublayer:self.preview atIndex:0]; 79 }); 80 } 81 } 82 83 // Start 84 if (_canOpen) { 85 dispatch_async(dispatch_get_main_queue(), ^{ 86 // 回到主线程 87 timer = [NSTimer scheduledTimerWithTimeInterval:.02 target:self selector:@selector(lineAnimation) userInfo:nil repeats:YES]; 88 [_session startRunning]; 89 }); 90 } 91 }); 92 } 93 94 // 处理数据 95 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{ 96 97 NSString *stringValue;// 扫码结果 98 if ([metadataObjects count] >0){ 99 100 // 播放扫描二维码的声音 101 SystemSoundID soundID; 102 NSString *strSoundFile = [[NSBundle mainBundle] pathForResource:@"noticeMusic" ofType:@"wav"]; 103 AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:strSoundFile],&soundID); 104 AudioServicesPlaySystemSound(soundID); 105 106 // 处理扫码结果 107 AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjects objectAtIndex:0]; 108 stringValue = metadataObject.stringValue; 109 } 110 111 // 停止扫码 112 [timer invalidate]; 113 timer = nil; 114 [_session stopRunning]; 115 116 UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"扫描结果" message:stringValue preferredStyle:UIAlertControllerStyleAlert]; 117 UIAlertAction *OKaction = [UIAlertAction actionWithTitle:@"继续扫码" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 118 dispatch_async(dispatch_get_main_queue(), ^{ 119 // 回到主线程 120 timer = [NSTimer scheduledTimerWithTimeInterval:.02 target:self selector:@selector(lineAnimation) userInfo:nil repeats:YES]; 121 [_session startRunning]; 122 }); 123 124 }]; 125 [alertVC addAction:OKaction]; 126 [self presentViewController:alertVC animated:YES completion:nil]; 127 } 128 129 // 停止扫码 130 - (void)viewWillDisappear:(BOOL)animated{ 131 132 [super viewWillDisappear:animated]; 133 [timer invalidate]; 134 timer = nil; 135 [_session stopRunning]; 136 } 137 138 139 // 界面布局 140 - (void)creatUI{ 141 142 // 遮罩层 143 UIImageView *maskView = [[UIImageView alloc] initWithFrame:CGRectMake(0,0, MainScreen_W, MainScreen_H - 64)]; 144 maskView.backgroundColor = [UIColor clearColor]; 145 146 // 扫描区View 147 maskView.image = [UIImage maskImageWithMaskRect:maskView.frame clearRect:CGRectMake((MainScreen_W-MiddleWidth)/2, Top_Height, MiddleWidth, MiddleWidth)]; 148 [self.view addSubview:maskView]; 149 150 // 四角 151 CGFloat leadSpace = (MainScreen_W - MiddleWidth) / 2.0; 152 UIImageView * imageView = [[UIImageView alloc]initWithFrame:CGRectMake(leadSpace, Top_Height, MiddleWidth, MiddleWidth)]; 153 imageView.image = [UIImage imageNamed:@"code_corner.png"]; 154 [self.view addSubview:imageView]; 155 156 upOrdown = NO; 157 num = 0; 158 _line = [[UIImageView alloc] initWithFrame:CGRectMake(leadSpace, Top_Height, MiddleWidth, 12)]; 159 _line.image = [UIImage imageNamed:@"scanLineOn.png"]; 160 _line.contentMode = UIViewContentModeScaleToFill; 161 [self.view addSubview:_line]; 162 163 // 开关灯 button 164 UIButton * turnBtn = [UIButton buttonWithType:UIButtonTypeCustom]; 165 [turnBtn setBackgroundImage:[UIImage imageNamed:@"lampDown.png"] forState:UIControlStateNormal]; 166 [turnBtn setBackgroundImage:[UIImage imageNamed:@"lampOn.png"] forState:UIControlStateSelected]; 167 turnBtn.frame = CGRectMake((MainScreen_W-60)/2.0,Top_Height + MiddleWidth + 95,60,60); 168 [turnBtn addTarget:self action:@selector(turnBtnEvent:) forControlEvents:UIControlEventTouchUpInside]; 169 [self.view addSubview:turnBtn]; 170 } 171 172 // line 动画 173 -(void)lineAnimation{ 174 175 CGFloat leadSpace = (MainScreen_W - MiddleWidth)/ 2; 176 if (upOrdown == NO) { 177 num ++; 178 _line.frame = CGRectMake(leadSpace, Top_Height+2*num, MiddleWidth, 12); 179 if (2*num >= MiddleWidth-12) { 180 upOrdown = YES; 181 _line.image = [UIImage imageNamed:@"scanLineDown.png"]; 182 } 183 }else { 184 num --; 185 _line.frame = CGRectMake(leadSpace, Top_Height+2*num, MiddleWidth, 12); 186 if (num == 0) { 187 upOrdown = NO; 188 _line.image = [UIImage imageNamed:@"scanLineOn.png"]; 189 } 190 } 191 } 192 193 // 开/关判定状态 194 - (void)turnBtnEvent:(UIButton *)button_{ 195 196 button_.selected = !button_.selected; 197 if (button_.selected) { 198 [self turnTorchOn:YES]; 199 }else{ 200 [self turnTorchOn:NO]; 201 } 202 } 203 204 // 开/关灯 205 - (void)turnTorchOn:(bool)on{ 206 207 Class captureDeviceClass = NSClassFromString(@"AVCaptureDevice"); 208 if (captureDeviceClass != nil) { 209 AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 210 211 if ([device hasTorch] && [device hasFlash]){ 212 213 [device lockForConfiguration:nil]; 214 if (on) { 215 [device setTorchMode:AVCaptureTorchModeOn]; 216 [device setFlashMode:AVCaptureFlashModeOn]; 217 } else { 218 [device setTorchMode:AVCaptureTorchModeOff]; 219 [device setFlashMode:AVCaptureFlashModeOff]; 220 } 221 [device unlockForConfiguration]; 222 } 223 } 224 } 225 226 @end
4 - 代码示例:如何使用
// - ViewController.m:点击屏幕进入扫码页面
1 #import "ViewController.h" 2 #import "ScanQRcodeVC.h" 3 @implementation ViewController 4 5 - (void)viewDidLoad { 6 [super viewDidLoad]; 7 self.view.backgroundColor = [UIColor whiteColor]; 8 self.navigationController.navigationBar.translucent = NO;// 禁止毛玻璃效果 9 } 10 11 // 修改标题 12 -(void)viewWillAppear:(BOOL)animated{ 13 [super viewWillAppear:YES]; 14 self.navigationItem.title = @"轻点屏幕,进入扫码页面"; 15 16 } 17 18 -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ 19 20 ScanQRcodeVC *scVC = [[ScanQRcodeVC alloc] init]; 21 scVC.view.backgroundColor = [UIColor whiteColor]; 22 self.navigationItem.title = @"返回"; 23 [self.navigationController pushViewController:scVC animated:YES]; 24 } 25 26 27 @end
素材地址
https://pan.baidu.com/s/1fmB8bXRT6WaPjFh_PHlE_Q
1d7c
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)