一)使用场景
常见的扫码功能,提供扫描动画效果;
效果如下图所示!!!
二)源码解读
1)ScanCodeViewController.h文件
//ScanCodeViewController.h
#import
#import
NS_ASSUME_NONNULL_BEGIN
@interface ScanCodeViewController : UIViewController
@property (nonatomic,assign) BOOL isShowLight; //是否开启手电筒
@property (nonatomic, copy) void (^resultHandler)(NSString *resultCode,BOOL cancelled);
@end
NS_ASSUME_NONNULL_END
2)ScanCodeViewController.m文件
// ScanCodeViewController.m
#import "ScanCodeViewController.h"
#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
#define kMargin 40
#define kBorderW SCREEN_HEIGHT * 0.2
@interface ScanCodeViewController ()
@property (nonatomic,assign) BOOL lightOn; //手电筒开启状态
@property (nonatomic, strong) UILabel *lightTips; //提示文本
@property (nonatomic, strong) UIView *scanWindow; //扫码视图
@property (nonatomic, strong) UIImageView *scanNetImageView; //动画视图
@property (nonatomic, strong) CAAnimation *animation; //动画对象
@property (nonatomic, strong) AVCaptureSession *session; //扫码对象
@property (nonatomic, strong) AVCaptureDevice *captureDevice; //手电筒对象
@end
@implementation ScanCodeViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = @"扫描二维码";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"关闭"
style:UIBarButtonItemStyleDone
target:self
action:@selector(buttonCloseClick:)];
//1.扫描区域
[self setupScanWindowView];
//2.四周遮罩
[self setupMaskView];
//3.提示文本
[self showTipTitleView:self.isShowLight];
//4.初始化扫码对象
[self beginScanning];
}
- (void)buttonCloseClick:(id)sender
{
[self dismissViewControllerAnimated:YES
completion:nil];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:NO];
if (![_session isRunning])
{
[_session startRunning];
}
//开启扫码动画
[self resumeAnimation];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:NO];
if ([_session isRunning])
{
[_session stopRunning];
}
}
- (void)setupScanWindowView
{
CGFloat scanWindowH = SCREEN_WIDTH - kMargin * 2;
CGFloat scanWindowW = SCREEN_WIDTH - kMargin * 2;
_scanWindow = [[UIView alloc] initWithFrame:CGRectMake(kMargin, kBorderW, scanWindowW, scanWindowW)];
_scanWindow.backgroundColor = [UIColor clearColor];
_scanWindow.clipsToBounds = YES;
_scanWindow.layer.borderColor = [UIColor lightGrayColor].CGColor;
[self.view addSubview:_scanWindow];
_scanNetImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"scan_net"]];
CGFloat buttonWH = 18.0f;
CGFloat spacing = -1.0f;
UIButton *topLeft = [[UIButton alloc] initWithFrame:CGRectMake(spacing, spacing, buttonWH, buttonWH)];
[topLeft setImage:[UIImage imageNamed:@"QRCodeTopLeft"] forState:UIControlStateNormal];
[_scanWindow addSubview:topLeft];
UIButton *topRight = [[UIButton alloc] initWithFrame:CGRectMake(scanWindowW - buttonWH - spacing, spacing, buttonWH, buttonWH)];
[topRight setImage:[UIImage imageNamed:@"QRCodeTopRight"] forState:UIControlStateNormal];
[_scanWindow addSubview:topRight];
UIButton *bottomLeft = [[UIButton alloc] initWithFrame:CGRectMake(spacing, scanWindowH - buttonWH - spacing, buttonWH, buttonWH)];
[bottomLeft setImage:[UIImage imageNamed:@"QRCodeBottomLeft"] forState:UIControlStateNormal];
[_scanWindow addSubview:bottomLeft];
UIButton *bottomRight = [[UIButton alloc] initWithFrame:CGRectMake(topRight.frame.origin.x, bottomLeft.frame.origin.y, buttonWH, buttonWH)];
[bottomRight setImage:[UIImage imageNamed:@"QRCodeBottomRight"] forState:UIControlStateNormal];
[_scanWindow addSubview:bottomRight];
}
- (void)setupMaskView
{
UIColor *maskColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
UIView *leftMask = [[UIView alloc] init];
leftMask.frame = CGRectMake(0, 0, kMargin, SCREEN_HEIGHT);
[self.view addSubview:leftMask];
leftMask.backgroundColor = maskColor;
UIView *rightMask = [[UIView alloc] init];
rightMask.frame = CGRectMake(kMargin + _scanWindow.frame.size.width, 0, kMargin, SCREEN_HEIGHT);
[self.view addSubview:rightMask];
rightMask.backgroundColor = maskColor;
UIView *topMask = [[UIView alloc] init];
topMask.frame = CGRectMake(kMargin, 0, _scanWindow.frame.size.width, kBorderW);
[self.view addSubview:topMask];
topMask.backgroundColor = maskColor;
UIView *bottomMask = [[UIView alloc] init];
bottomMask.frame = CGRectMake(kMargin, kBorderW + _scanWindow.frame.size.height, _scanWindow.frame.size.width, SCREEN_HEIGHT - (kBorderW + _scanWindow.frame.size.height));
[self.view addSubview:bottomMask];
bottomMask.backgroundColor = maskColor;
}
-(void)showTipTitleView:(BOOL)display{
if (!display) return;
_lightTips = [UILabel new];
_lightTips.frame = CGRectMake(self.view.center.x - 100, SCREEN_HEIGHT - 160, 200, 80);
[self.view addSubview:_lightTips];
_lightTips.font = [UIFont boldSystemFontOfSize:18];
_lightTips.text = @"打开手电筒";
_lightTips.textAlignment = NSTextAlignmentCenter;
_lightTips.textColor = [UIColor whiteColor];
UIButton *lightBtn = [UIButton buttonWithType:UIButtonTypeCustom];
lightBtn.frame = CGRectMake(self.view.center.x - 30, SCREEN_HEIGHT - 200 , 60, 60);
[self.view addSubview:lightBtn];
[lightBtn setBackgroundImage:[UIImage imageNamed:@"flashlight_icon"] forState:UIControlStateNormal];
[lightBtn addTarget:self action:@selector(openFlash:) forControlEvents:UIControlEventTouchUpInside];
}
- (AVCaptureDevice *)captureDevice{
if (!_captureDevice) {
self.captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
}
return _captureDevice;
}
#pragma mark-> 手电筒
-(void)openFlash:(UIButton*)button{
if (![self.captureDevice hasTorch]) {
return;
}
_lightOn = !_lightOn;
if (_lightOn) {
_lightTips.text = @"关闭手电筒";
//开启手电筒
[self.captureDevice lockForConfiguration:nil];
[self.captureDevice setTorchMode:AVCaptureTorchModeOn];
[self.captureDevice unlockForConfiguration];
}else{
_lightTips.text = @"打开手电筒";
//关闭手电筒
[self.captureDevice lockForConfiguration:nil];
[self.captureDevice setTorchMode:AVCaptureTorchModeOff];
[self.captureDevice unlockForConfiguration];
}
}
- (void)beginScanning
{
//获取摄像设备
AVCaptureDevice * device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
//创建输入流
AVCaptureDeviceInput * input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
if (!input) return;
//创建输出流
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];
AVCaptureVideoPreviewLayer * layer = [AVCaptureVideoPreviewLayer layerWithSession:_session];
layer.videoGravity=AVLayerVideoGravityResizeAspectFill;
layer.frame= CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
[self.view.layer insertSublayer:layer atIndex:0];
//开始捕获
[_session startRunning];
}
- (void)resumeAnimation
{
if (_scanNetImageView.isHidden == NO) {
_scanNetImageView.hidden = NO;
}
if(_animation){
// 1. 将动画的时间偏移量作为暂停时的时间点
CFTimeInterval pauseTime = _scanNetImageView.layer.timeOffset;
// 2. 根据媒体时间计算出准确的启动动画时间,对之前暂停动画的时间进行修正
CFTimeInterval beginTime = CACurrentMediaTime() - pauseTime;
// 3. 要把偏移时间清零
[_scanNetImageView.layer setTimeOffset:0.0];
// 4. 设置图层的开始动画时间
[_scanNetImageView.layer setBeginTime:beginTime];
[_scanNetImageView.layer setSpeed:1.0];
}else{
CGFloat scanNetImageViewH = 241;
CGFloat scanWindowH = self.view.frame.size.width - kMargin * 2;
CGFloat scanNetImageViewW = _scanWindow.frame.size.width;
_scanNetImageView.frame = CGRectMake(0, -scanNetImageViewH, scanNetImageViewW, scanNetImageViewH);
CABasicAnimation *scanNetAnimation = [CABasicAnimation animation];
scanNetAnimation.keyPath = @"transform.translation.y";
scanNetAnimation.byValue = @(scanWindowH);
scanNetAnimation.duration = 1.0;
scanNetAnimation.repeatCount = MAXFLOAT;
[_scanNetImageView.layer addAnimation:scanNetAnimation forKey:@"translationAnimation"];
[_scanWindow addSubview:_scanNetImageView];
}
}
#pragma mark -> GetResuleDelegate
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
//扫码完成之后禁用动画
_scanNetImageView.hidden = YES;
//停止捕获
[_session stopRunning];
NSString *stringValue;
if (metadataObjects.count > 0) {
AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjects objectAtIndex : 0 ];
stringValue = metadataObject.stringValue;
if (self.resultHandler) self.resultHandler(stringValue,NO);
}else{
if (self.resultHandler) self.resultHandler(stringValue,YES);
}
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
三)资源文件
1)扫码背景网格图