iOS高仿微信二維碼掃描

最近有時(shí)間研究下iOS下的二維碼掃描索守,以前是用ZBar做掃描的,現(xiàn)在用系統(tǒng)原生的AVFoundation框架也可以做到了,下面分享一下自己的實(shí)現(xiàn)過程:

1.首先演示一下具體效果:

演示地址
演示地址

2. 下面說一下實(shí)現(xiàn)過程:

. 1 首先需要創(chuàng)建二維碼掃描對象

// 顯示掃描后的結(jié)果
@property (weak, nonatomic) IBOutlet UILabel *resultLab;

// 高度約束
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *containerHeightCons;

// 掃描線
@property (weak, nonatomic) IBOutlet UIImageView *scanLineView;

// 掃描線的約束,這里很重要刻肄,動畫效果主要是根據(jù)設(shè)置這個的值實(shí)現(xiàn)的
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *scanLineCons;

// 自定義ToolBar
@property (weak, nonatomic) IBOutlet UITabBar *customTabBar;

// 會話
@property (nonatomic, strong) AVCaptureSession *session;

// 輸入設(shè)備
@property (nonatomic, strong) AVCaptureDeviceInput *deviceInput;

// 輸出設(shè)備
@property (nonatomic, strong) AVCaptureMetadataOutput *output;

// 預(yù)覽圖層
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;

// 會話圖層
@property (nonatomic, strong) CALayer *drawLayer;

. 2 然后對這些對象進(jìn)行懶加載:

#pragma mark - 懶加載
// 會話
- (AVCaptureSession *)session
{
    if (_session == nil) {
        _session = [[AVCaptureSession alloc] init];
    }
    return _session;
}
// 拿到輸入設(shè)備
- (AVCaptureDeviceInput *)deviceInput
{
    if (_deviceInput == nil) {
        AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        
        _deviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:device error:nil];
    }
    return _deviceInput;
}
// 拿到輸出對象
- (AVCaptureMetadataOutput *)output
{
    if (_output == nil) {
        _output = [[AVCaptureMetadataOutput alloc] init];
    }
    return _output;
}
// 創(chuàng)建預(yù)覽圖層
- (AVCaptureVideoPreviewLayer *)previewLayer
{
    if (_previewLayer == nil) {
        _previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
        _previewLayer.frame = [UIScreen mainScreen].bounds;
    }
    return _previewLayer;
}
// 創(chuàng)建用于繪制邊線的圖層
- (CALayer *)drawLayer
{
    if (_drawLayer == nil) {
        _drawLayer = [[CALayer alloc] init];
        _drawLayer.frame = [UIScreen mainScreen].bounds;
    }
    return _drawLayer;
}

. 3 創(chuàng)建完對象之后,我們先實(shí)現(xiàn)二維碼掃描的動態(tài)效果融欧,然后再去執(zhí)行掃描二維碼的操作:

// 這里主要是通過設(shè)置約束敏弃,讓掃描線不停的執(zhí)行動畫
- (void)startAnimation
{
    // 讓約束從頂部開始
    self.scanLineCons.constant = 0;
    [self.view layoutIfNeeded];

    // 設(shè)置動畫指定的次數(shù)

    [UIView animateWithDuration:2.0 animations:^{
        // 1.修改約束
        self.scanLineCons.constant = self.containerHeightCons.constant;
        
        [UIView setAnimationRepeatCount:MAXFLOAT];
        
        // 2.強(qiáng)制更新界面
        [self.view layoutIfNeeded];
    }];
}

. 4 動畫實(shí)現(xiàn)之后,看了微信的二維碼掃描之后噪馏,發(fā)現(xiàn)下面有個toolbar麦到,可以掃描不同的類型,不同類型需要改變掃描View的高度逝薪,實(shí)現(xiàn)過程如下

/**
 *  選擇tabBar時(shí)進(jìn)行跳轉(zhuǎn)
 *
 *  @param tabBar tabbar
 *  @param item   tabBar的item
 */
- (void)tabBar:(UITabBar *)tabBar didSelectItem:(UITabBarItem *)item
{
    if (item.tag == 1) {
        self.containerHeightCons.constant = 300;
    } else {
        self.containerHeightCons.constant = 150;
    }
    
    // 2.停止動畫
    [self.view.layer removeAllAnimations];
    [self.scanLineView.layer removeAllAnimations];
    
    // 3.重新開始動畫
    [self startAnimation];
}

. 4 下面是最重要的一步了隅要,就要開始二維碼掃描,這里需要判斷輸入輸出會話是否可以正確添加董济,添加完成之后步清,需要設(shè)置二維碼支持的類型,一般默認(rèn)是全部虏肾,然后還需要添加預(yù)覽圖層:

- (void)startScan
{
    // 1.判斷是否能夠?qū)⑤斎胩砑拥綍捴?    if (![self.session canAddInput:self.deviceInput]) {
        return;
    }
    
    // 2.判斷是否能夠?qū)⑤敵鎏砑拥綍捴?    if (![self.session canAddOutput:self.output]) {
        return;
    }
    
    // 3.將輸入和輸出都添加到會話中
    [self.session addInput:self.deviceInput];
    
    [self.session addOutput:self.output];
    
    // 4.設(shè)置輸出能夠解析的數(shù)據(jù)類型
    // 注意: 設(shè)置能夠解析的數(shù)據(jù)類型, 一定要在輸出對象添加到會員之后設(shè)置, 否則會報(bào)錯
    self.output.metadataObjectTypes = self.output.availableMetadataObjectTypes;
    [self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    
    // 如果想實(shí)現(xiàn)只掃描一張圖片, 那么系統(tǒng)自帶的二維碼掃描是不支持的
    // 只能設(shè)置讓二維碼只有出現(xiàn)在某一塊區(qū)域才去掃描
//    self.output.rectOfInterest = CGRectMake(0.0, 0.0, 1, 1);
    
    // 5.添加預(yù)覽圖層
    [self.view.layer insertSublayer:self.previewLayer atIndex:0];
    
    // 添加繪制圖層
    [self.previewLayer addSublayer:self.drawLayer];
    
    // 6.告訴session開始掃描
    [self.session startRunning];
}

. 5 二維碼掃描獲得信息時(shí)廓啊,就會執(zhí)行下面的代理方法,我們可以在下面的代理方法中執(zhí)行我們需要執(zhí)行的操作:

/**
 *  當(dāng)從二維碼中獲取到信息時(shí)封豪,就會調(diào)用下面的方法
 *
 *  @param captureOutput   輸出對象
 *  @param metadataObjects 信息
 *  @param connection
 */
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    // 0.清空圖層
    [self clearCorners];
    
    if (metadataObjects.count == 0 || metadataObjects == nil) {
        return;
    }
    
    // 1.獲取掃描到的數(shù)據(jù)
    // 注意: 要使用stringValue
    self.resultLab.text = [metadataObjects.lastObject stringValue];
    [self.resultLab sizeToFit];
    
    // 2.獲取掃描到的二維碼的位置
    // 2.1轉(zhuǎn)換坐標(biāo)
    for (AVMetadataObject *object in metadataObjects) {
        // 2.1.1判斷當(dāng)前獲取到的數(shù)據(jù), 是否是機(jī)器可識別的類型
        if ([object isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) {
            // 2.1.2將坐標(biāo)轉(zhuǎn)換界面可識別的坐標(biāo)
            AVMetadataMachineReadableCodeObject *codeObject = (AVMetadataMachineReadableCodeObject *)[self.previewLayer transformedMetadataObjectForMetadataObject:object];
            // 2.1.3繪制圖形
            [self drawCorners:codeObject];
        }
    }
}

. 6 對掃描到的二維碼谴轮,我們可以畫出它的具體位置:

/**
 *  畫出二維碼的邊框
 *
 *  @param codeObject 保存了坐標(biāo)的對象
 */
- (void)drawCorners:(AVMetadataMachineReadableCodeObject *)codeObject
{
    if (codeObject.corners.count == 0) {
        return;
    }
    
    // 1.創(chuàng)建一個圖層
    CAShapeLayer *layer = [[CAShapeLayer alloc] init];
    layer.lineWidth = 4;
    layer.strokeColor = [UIColor redColor].CGColor;
    layer.fillColor = [UIColor clearColor].CGColor;
    
    // 2.創(chuàng)建路徑
    UIBezierPath *path = [[UIBezierPath alloc] init];
    CGPoint point = CGPointZero;
    NSInteger index = 0;
    
    // 2.1移動到第一個點(diǎn)
    // 從corners數(shù)組中取出第0個元素, 將這個字典中的x/y賦值給point
    CGPointMakeWithDictionaryRepresentation((CFDictionaryRef)codeObject.corners[index++], &point);
    [path moveToPoint:point];
    
    // 2.2移動到其它的點(diǎn)
    while (index < codeObject.corners.count) {
        CGPointMakeWithDictionaryRepresentation((CFDictionaryRef)codeObject.corners[index++], &point);
        [path addLineToPoint:point];
    }
    // 2.3關(guān)閉路徑
    [path closePath];
    
    // 2.4繪制路徑
    layer.path = path.CGPath;
    
    // 3.將繪制好的圖層添加到drawLayer上
    [self.drawLayer addSublayer:layer];
}

/**
 *  清除邊線
 */
- (void)clearCorners
{
    if (self.drawLayer.sublayers == nil || self.drawLayer.sublayers.count == 0) {
        return;
    }
    
    for (CALayer *subLayer in self.drawLayer.sublayers) {
        [subLayer removeFromSuperlayer];
    }
}

3.這里面也有一些注意事項(xiàng):

. 確保輸入輸出對象被正確的添加到會話中

. 實(shí)現(xiàn)動畫主要是設(shè)置掃描線的頂部距離父控件的頂部的高度實(shí)現(xiàn)的

4.下面分享一下源代碼:

github地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市吹埠,隨后出現(xiàn)的幾起案子第步,更是在濱河造成了極大的恐慌,老刑警劉巖缘琅,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件粘都,死亡現(xiàn)場離奇詭異,居然都是意外死亡刷袍,警方通過查閱死者的電腦和手機(jī)翩隧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來呻纹,“玉大人堆生,你說我怎么就攤上這事±桌遥” “怎么了淑仆?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長太闺。 經(jīng)常有香客問我糯景,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任蟀淮,我火速辦了婚禮最住,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘怠惶。我一直安慰自己涨缚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布策治。 她就那樣靜靜地躺著脓魏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪通惫。 梳的紋絲不亂的頭發(fā)上茂翔,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機(jī)與錄音履腋,去河邊找鬼珊燎。 笑死,一個胖子當(dāng)著我的面吹牛遵湖,可吹牛的內(nèi)容都是我干的悔政。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼延旧,長吁一口氣:“原來是場噩夢啊……” “哼谋国!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起迁沫,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤芦瘾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后集畅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體旅急,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年牡整,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片溺拱。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡逃贝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出迫摔,到底是詐尸還是另有隱情沐扳,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布句占,位于F島的核電站沪摄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜杨拐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一祈餐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧哄陶,春花似錦帆阳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至至扰,卻和暖如春鳍徽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背敢课。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工阶祭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人翎猛。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓胖翰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親切厘。 傳聞我的和親對象是個殘疾皇子萨咳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

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