扫码功能动画效果(iOS)

一)使用场景

常见的扫码功能,提供扫描动画效果;
效果如下图所示!!!


IMG_0184.PNG
二)源码解读

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)扫码背景网格图


你可能感兴趣的:(扫码功能动画效果(iOS))