原生二维码扫描

import "ScanViewController.h"

#import <AVFoundation/AVFoundation.h>
#import "LayoutView.h"

#define kZero 0
#define kFullScreen [UIScreen mainScreen].bounds
#define kFullWidth [UIScreen mainScreen].bounds.size.width
#define kFullHeight [UIScreen mainScreen].bounds.size.height

@interface ScanViewController ()    <AVCaptureMetadataOutputObjectsDelegate>

@property (nonatomic ,strong) AVCaptureSession *session;
@property (nonatomic, assign) BOOL isOpenFlashlight;//是否打开闪光灯

@end

@implementation ScanViewController
-(void)isOpenFlashLight:(UIButton *)btn{
self.isOpenFlashlight = !self.isOpenFlashlight;
 AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];


if (self.isOpenFlashlight) {//打开闪光灯
    if (device.torchMode == AVCaptureTorchModeOff) {//torch手电筒模式
          AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        if (device.position == AVCaptureDevicePositionFront){
                return;
            }else{
                [device lockForConfiguration:nil];//设备锁
                [device setTorchMode:AVCaptureTorchModeOn];//手电筒打开
                [device unlockForConfiguration];//不需要自动调用
            }
        
    }
}else{
    if (device.torchMode == AVCaptureTorchModeOn) {//手电筒打开
        [device lockForConfiguration:nil];
        [device setTorchMode:AVCaptureTorchModeOff];
        [device unlockForConfiguration];
    }
}



}
//FIXME:MARK --- 摄像头前后切换 --
-(void)switchCameraBtnClick:(UIButton *)btn {
[self swapFontAndBackCameras];
}
//FIXME:MARK -- 是否有摄像头 ---
-(BOOL)isCameraAvaliable {
return [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
}
//FIXME:MARK -- 前置摄像头是否可用 --
- (BOOL)isFrontCameraAvailable {

return [UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront];
}
//FIXME:MARK -- 后置摄像头是否可用--
-(BOOL)isRearCameraAvailable {
return [UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear];
}
//FIXME:MARK -- 是否有其他摄像设备 --
-(BOOL)hasMultipleCameras {
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
if (devices != nil && [devices count] > 1) {
    return YES;
}else{
    return NO;
}
}
//FIXME:MARK -- 设备方向 --
-(AVCaptureDevice *)cameraWithPositon:(AVCaptureDevicePosition )position {

NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *device in devices ) {
    if (device.position == position) {
        return device;
    }
}
    return nil;
}
//FIXME:MARK -- 设置摄像头方向--
-(void)swapFontAndBackCameras {

if (![self hasMultipleCameras]) {
    return;
}

NSArray *inputs = self.session.inputs;
for (AVCaptureDeviceInput *input in inputs) {
    AVCaptureDevice *device = input.device;
    if ([device hasMediaType:AVMediaTypeVideo]) {
        
        AVCaptureDevicePosition position = device.position;
        AVCaptureDevice *newCamera = nil;
        AVCaptureDeviceInput *newInput = nil;
        
        if (position == AVCaptureDevicePositionFront){
            //前置状态闪光等自动关闭
            if (device.torchMode == AVCaptureTorchModeOn) {
                [device setTorchMode:AVCaptureTorchModeOff];
            }else{
                NSLog(@"do nothing");
            }
             newCamera = [self cameraWithPositon:AVCaptureDevicePositionBack];
        }
        else{
            newCamera = [ self cameraWithPositon:AVCaptureDevicePositionFront];
            
           
        }
         newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil];
        
        [self.session beginConfiguration];
        [self.session removeInput:input];
        [self.session addInput:newInput];
        
        //提交 防止配置无效
        [self.session commitConfiguration];
        break;
    }
    
}
}
- (void)viewDidLoad {
[super viewDidLoad];
//是否开启闪光灯
self.isOpenFlashlight = NO;
self.automaticallyAdjustsScrollViewInsets = NO;

//相机授权
[self checkAVAuthorizationStatus];

LayoutView *layout = [[LayoutView alloc]initWithFrame:CGRectMake(kZero, 64, kFullWidth, kFullHeight - 64)];
[self.view addSubview:layout];


UIButton *flashBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[flashBtn setTitle:@"闪光灯" forState:UIControlStateNormal];
flashBtn.frame = CGRectMake(80, kFullHeight - 70, 60, 40);
[flashBtn addTarget:self action:@selector(isOpenFlashLight:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:flashBtn];

UIButton *switchCameraBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[switchCameraBtn setTitle:@"前后切换" forState:UIControlStateNormal];
switchCameraBtn.frame = CGRectMake(180, kFullHeight - 70, 60, 40);
[switchCameraBtn addTarget:self action:@selector(switchCameraBtnClick:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:switchCameraBtn];

//呈现图片的一个图层
AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
layer.frame = self.view.layer.bounds;
[self.view.layer insertSublayer:layer atIndex:0];

                                     
}

/*
*  AVAuthorizationStatus
*
*  @param  AVAuthorizationStatusRestricted         授权请求
 *  @param  AVAuthorizationStatusDenied             授权失败
 *  @param  AVAuthorizationStatusAuthorized         授权成功
 */

-(void)checkAVAuthorizationStatus{

AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (status == AVAuthorizationStatusRestricted) {
    //客户端未授权
    
}else if (status == AVAuthorizationStatusDenied){
    //用户明确拒绝访问
    UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"请在设置中开启照相机权限" message:nil preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
        
    }];
    [alertVC addAction:action];
    [self presentViewController:alertVC animated:YES completion:nil];
}else if (status == AVAuthorizationStatusAuthorized){
    //授权
}
}
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.session startRunning];
self.tabBarController.tabBar.hidden = YES;
}
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[self.session stopRunning];
}
-(AVCaptureSession *)session{
if (!_session) {
    //获取摄像设备
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    //创建输入流
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
    if (!input) {
        return  nil;
    }
    //创建输出流
    AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc]init];
    //设置代理,在主线程里刷新
    [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    //设置扫描区域的比例
    CGFloat width = 300/CGRectGetWidth(self.view.bounds);
    CGFloat height = 300/CGRectGetHeight(self.view.bounds);
    //此属性的值是一个CGRect指定一个rectofinterest可能提高某些类型的元数据的检测性能。此属性的默认值是价值cgrectmake(0,0,1,1)。元数据对象的边界不与rectofinterest相交将不会返回
#warning rectOfInterest --> (y,x,height,width)且默认(0,0,1,1)

    output.rectOfInterest = CGRectMake((1-height)/2, (1-width)/2, height, width);
    
    self.session = [[AVCaptureSession alloc]init];
    //高质量采集率
    [self.session setSessionPreset:AVCaptureSessionPresetHigh];
    [self.session addInput:input];
    [self.session addOutput:output];
    
    //设置扫码支持的编码格式(条形码和二维码兼容)
    output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode,
                                   AVMetadataObjectTypeEAN13Code,
                                   AVMetadataObjectTypeEAN8Code,
                                   AVMetadataObjectTypeCode128Code];
}
return _session;
}
pragma mark **** AVCaptureMetadataOutputObjectsDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
NSLog(@"%@",metadataObjects);
if (metadataObjects.count > 0) {
    [self.session stopRunning];
    
    AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects firstObject];
#pragma mark * 扫描结果事件
    UIAlertController *alertC = [UIAlertController alertControllerWithTitle:@"扫码结果" message:[metadataObject stringValue] preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
        [self.session startRunning];
    }];
    [alertC addAction:action];
    [self presentViewController:alertC animated:YES completion:nil];
}
}

@end

02.滚动线的View的编写

#import "LayoutView.h"

@interface LayoutView ()

@property (nonatomic, strong) CALayer *lineLabyer;


@end

@implementation LayoutView

-(instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
    [self setLayoutView];
}
return self;
}
//重绘view方法
-(void)drawRect:(CGRect)rect{

CGFloat width = rect.size.width;
CGFloat height = rect.size.height;
CGFloat pickingFieldWidth = 300;//采集信息区域的大小
CGFloat pickingFieldHieght = 300;

//创建上下文
CGContextRef contectRef = UIGraphicsGetCurrentContext();
//将当前图形状态推入堆栈,之后,您对图形状态所做的修改会影响随后的描画操作,但不影响存储在堆栈中的拷贝
CGContextSaveGState(contectRef);
//设置填充颜色
CGContextSetRGBFillColor(contectRef, 0, 0, 0, 0.35);
//线的宽度
CGContextSetLineWidth(contectRef, 3);

CGRect pickingFieldRect = CGRectMake((width-pickingFieldWidth)/2, (height-pickingFieldWidth)/2, pickingFieldWidth, pickingFieldHieght);
//根据一个矩形划线
UIBezierPath *pickingFieldPath = [UIBezierPath bezierPathWithRect:pickingFieldRect];
UIBezierPath *bezierPathRect = [UIBezierPath bezierPathWithRect:rect];

[bezierPathRect appendPath:pickingFieldPath];


//填充使用奇偶法则(NO为非0法则)
bezierPathRect.usesEvenOddFillRule = YES;
//填充
[bezierPathRect fill];
CGContextSetLineWidth(contectRef, 2);
CGContextSetRGBStrokeColor(contectRef, 27/255.0, 181/255.0, 254/255.0, 1);

[pickingFieldPath stroke];//划线
CGContextRestoreGState(contectRef);
//集中在水平和垂直方向的矩形
self.layer.contentsGravity = kCAGravityCenter;

}
-(void)setLayoutView{

self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
self.lineLabyer = [CALayer layer];
self.lineLabyer.contents = (__bridge id _Nullable)(([UIImage imageNamed:@"line"].CGImage));
[self.layer addSublayer:self.lineLabyer];

[self resumeAnimation];


self.lineLabyer.frame = CGRectMake((self.frame.size.width - 300)/2, (self.frame.size.height - 300)/2, 300, 2);

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resumeAnimation) name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(stopAnimation) name:UIApplicationDidEnterBackgroundNotification object:nil];

}

-(void)resumeAnimation{

CABasicAnimation *basic = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
basic.fromValue = @(0);
basic.toValue = @(300);
basic.duration = 1.5;
basic.repeatCount = NSIntegerMax;
[self.lineLabyer addAnimation:basic forKey:@"translationY"];
}
-(void)stopAnimation{
[self.lineLabyer removeAnimationForKey:@"translationY"];
}
@end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容