iOS- 掃描二維碼

自iOS7以后巡蘸,iOS掃描二維碼就并不需要借助第三方的框架,這次項(xiàng)目的需求擂送,自己實(shí)現(xiàn)一個(gè)掃描的功能悦荒,蘋果在AVFoundation中原生支持掃描二維碼的API,主要有5個(gè)類嘹吨,這5個(gè)類在自定義相機(jī)或者視頻時(shí)都用的上搬味,這5個(gè)類分別是:

1.AVCaptureDevice:代表抽象的硬件設(shè)備
2.AVCaptureSession:會(huì)話對(duì)象,連接輸入設(shè)備和輸出設(shè)備蟀拷。
3.AVCaptureDeviceInput:輸入設(shè)備碰纬。設(shè)備輸入數(shù)據(jù)管理對(duì)象,可以根據(jù)AVCaptureDevice創(chuàng)建對(duì)應(yīng)的AVCaptureDeviceInput對(duì)象问芬,該對(duì)象將會(huì)被添加到AVCaptureSession中管理
4.AVCaptureOutput:輸出設(shè)備悦析。輸出設(shè)備管理對(duì)象,用于接收各類輸出數(shù)據(jù)此衅,有很多子類强戴,每一個(gè)子類用途都不一樣亭螟,該對(duì)象將會(huì)被添加到AVCaptureSession中管理
5.AVCaptureVideoPreviewLayer:相機(jī)拍攝預(yù)覽圖層,是CALayer的子類骑歹,使用該對(duì)象可以實(shí)時(shí)的查看拍照或視屏錄制的效果预烙,設(shè)置好尺寸后需要添加到父view的layer中

核心的步驟:

  • 1.創(chuàng)建AVCaptureSession會(huì)話

  • 2.創(chuàng)建AVCaptureDevice設(shè)備

  • 3.創(chuàng)建輸入AVCaptureDeviceInput與輸出設(shè)備AVCaptureOutput,并添加到上面的繪畫中

  • 4.創(chuàng)建預(yù)覽層

  • 5.設(shè)置掃描區(qū)域

具體的代碼如下:

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

@interface ViewController ()<AVCaptureMetadataOutputObjectsDelegate,CALayerDelegate>

@property (weak, nonatomic) IBOutlet UIView *scanView;

@property (weak, nonatomic) IBOutlet UIImageView *scanline;
//結(jié)果
@property (weak, nonatomic) IBOutlet UILabel *result;
/**掃描區(qū)域的高度約束(高寬一致)*/
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *scanViewH;
//掃描線頂部的約束
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *scanlineTop;


@property (nonatomic,strong) CALayer *maskLayer;

/**
 *五個(gè)類
 */
@property (nonatomic,strong)AVCaptureDevice *device;

@property (nonatomic,strong)AVCaptureDeviceInput *input;

@property (nonatomic,strong) AVCaptureMetadataOutput *output;

@property (nonatomic,strong) AVCaptureSession *session;

@property (nonatomic,strong) AVCaptureVideoPreviewLayer *layer;

@end

@implementation ViewController

#pragma mark - 懶加載

-(AVCaptureDevice *)device{
    if (_device == nil) {
        
        _device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        
    }
    return _device;
    
}

-(AVCaptureDeviceInput *)input{
    
    if (_input == nil) {
        
        _input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil];
        
    }
    
    return  _input;
    
}

-(AVCaptureMetadataOutput *)output{
    
    if (_output == nil) {
        
        _output = [[AVCaptureMetadataOutput alloc]init];
        
        [_output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
        
    }
    
    return  _output;
}

#pragma mark - ViewController生命周期
/**
 *  掃描的那條線動(dòng)起來
 */
-(void)viewDidAppear:(BOOL)animated{
    
    [super viewDidAppear:animated];
    
    [UIView animateKeyframesWithDuration:3.0 delay:0 options:UIViewKeyframeAnimationOptionRepeat  animations:^{
        self.scanlineTop.constant = self.scanViewH.constant-4;
        [self.view layoutIfNeeded];
    } completion:^(BOOL finished) {
        
    }];
    
}


//-(void)viewWillAppear:(BOOL)animated{
//
//    [super viewWillAppear:animated];
//
//    [UIView animateWithDuration:3.0 delay:0 options:UIViewAnimationOptionRepeat animations:^{
//
//        self.scanlineTop.constant = self.scanViewH.constant - 4;
//
//        [self.scanline layoutIfNeeded];
//
//    } completion:nil];
//
//}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    //1道媚、創(chuàng)建會(huì)話
    AVCaptureSession *session = [[AVCaptureSession alloc]init];
    
    if ([session canSetSessionPreset:AVCaptureSessionPresetHigh]) {
        
        [session setSessionPreset:AVCaptureSessionPresetHigh];
        
    }
    
    //2扁掸、添加輸入和輸出設(shè)備
    if([session canAddInput:self.input]){
        
        [session addInput:self.input];
        
    }
    if([session canAddOutput:self.output]){
        
        [session addOutput:self.output];
    }
    
    
    //3、設(shè)置這次掃描的數(shù)據(jù)類型
    self.output.metadataObjectTypes = self.output.availableMetadataObjectTypes;
    
    //4最域、創(chuàng)建預(yù)覽層
    AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:session];
    
    layer.frame = self.view.bounds;
    
    [self.view.layer insertSublayer:layer atIndex:0];
    
    
    //5谴分、創(chuàng)建周圍的遮罩層
    CALayer *maskLayer = [[CALayer alloc]init];
    
    maskLayer.frame = self.view.bounds;
    
    //此時(shí)設(shè)置的顏色就是中間掃描區(qū)域最終的顏色
    maskLayer.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:0.2].CGColor;
    
    maskLayer.delegate = self;
    
    [self.view.layer insertSublayer:maskLayer above:layer];
    
    //讓代理方法調(diào)用 將周圍的蒙版顏色加深
    [maskLayer setNeedsDisplay];
    
    
    //6、關(guān)鍵設(shè)置掃描的區(qū)域 方法一:自己計(jì)算
    //    CGFloat x = (self.view.bounds.size.width - self.scanViewH.constant) * 0.5;
    //
    //    CGFloat y = (self.view.bounds.size.height- self.scanViewH.constant) * 0.5;
    //
    //    CGFloat w = self.scanViewH.constant;
    //
    //    CGFloat h = w;
    //
    //
    //    self.output.rectOfInterest = CGRectMake(y/self.view.bounds.size.height, x/self.view.bounds.size.width,  h/self.view.bounds.size.height, w/self.view.bounds.size.width);
    
    //6镀脂、關(guān)鍵設(shè)置掃描的區(qū)域狸剃,方法二:直接轉(zhuǎn)換,但是要在 AVCaptureInputPortFormatDescriptionDidChangeNotification 通知里設(shè)置,否則 metadataOutputRectOfInterestForRect: 轉(zhuǎn)換方法會(huì)返回 (0, 0, 0, 0)狗热。
    
    __weak __typeof(&*self)weakSelf = self;
    
    [[NSNotificationCenter defaultCenter] addObserverForName:AVCaptureInputPortFormatDescriptionDidChangeNotification object:nil queue:[NSOperationQueue currentQueue] usingBlock: ^(NSNotification *_Nonnull note) {
        
        weakSelf.output.rectOfInterest = [weakSelf.layer metadataOutputRectOfInterestForRect:self.scanView.frame];
        
    }];
    
    
    //7钞馁、開始掃描
    [session startRunning];
    
    
    self.session = session;
    self.layer = layer;
    self.maskLayer = maskLayer;
}

-(void)dealloc{
    
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
}


#pragma mark - 代理方法
/**
 *  如果掃描到了二維碼 回調(diào)該代理方法
 */
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
    
    if(metadataObjects.count > 0 && metadataObjects != nil){
        
        
        AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects lastObject];
        
        NSString *result = metadataObject.stringValue;
        
        self.result.text = result;
        
        [self.session stopRunning];
        
        [self.scanline removeFromSuperview];
        
    }
    
    
}

/**
 *   蒙版中間一塊要空出來
 */

-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
    
    if (layer == self.maskLayer) {
        
        UIGraphicsBeginImageContextWithOptions(self.maskLayer.frame.size, NO, 1.0);
        
        //蒙版新顏色
        CGContextSetFillColorWithColor(ctx, [UIColor colorWithRed:0 green:0 blue:0 alpha:0.6].CGColor);
        //補(bǔ)充當(dāng)前填充顏色的rect
        CGContextFillRect(ctx, self.maskLayer.frame);
        
        //轉(zhuǎn)換坐標(biāo)
        CGRect scanFrame = [self.view convertRect:self.scanView.frame fromView:self.scanView.superview];
        
        //空出中間一塊
        CGContextClearRect(ctx, scanFrame);
    }
}

@end

特別說明

在iOS10的權(quán)限問題,因?yàn)閕OS10對(duì)權(quán)限的要求更高匿刮,如果我們不設(shè)置相應(yīng)的權(quán)限問題僧凰,會(huì)直接導(dǎo)致程序崩潰,這時(shí)熟丸,我們需要在plist文件中加上相機(jī)權(quán)限的描述:

<key>NSCameraUsageDescription</key>
 <string>cameraDesciption</string>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末训措,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子光羞,更是在濱河造成了極大的恐慌绩鸣,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纱兑,死亡現(xiàn)場(chǎng)離奇詭異呀闻,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)潜慎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門捡多,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人铐炫,你說我怎么就攤上這事垒手。” “怎么了倒信?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵科贬,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我鳖悠,道長(zhǎng)榜掌,這世上最難降的妖魔是什么鸭丛? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮唐责,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瘾带。我一直安慰自己鼠哥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布看政。 她就那樣靜靜地躺著朴恳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪允蚣。 梳的紋絲不亂的頭發(fā)上于颖,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音嚷兔,去河邊找鬼森渐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛冒晰,可吹牛的內(nèi)容都是我干的同衣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼壶运,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼耐齐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蒋情,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤埠况,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后棵癣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辕翰,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年狈谊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了金蜀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡的畴,死狀恐怖渊抄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情丧裁,我是刑警寧澤护桦,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站煎娇,受9級(jí)特大地震影響二庵,放射性物質(zhì)發(fā)生泄漏贪染。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一催享、第九天 我趴在偏房一處隱蔽的房頂上張望杭隙。 院中可真熱鬧,春花似錦因妙、人聲如沸痰憎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)铣耘。三九已至,卻和暖如春以故,著一層夾襖步出監(jiān)牢的瞬間蜗细,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工怒详, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留炉媒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓昆烁,卻偏偏與公主長(zhǎng)得像橱野,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子善玫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容