iOS--二維碼的掃描

前面我們介紹了二維碼的生成坟冲,現在我們介紹一下怎么掃描二維碼舞箍;在iOS7之前,大部分應用中使用的二維碼掃描是ZXing或者ZBar第三方的掃描框架蜂大。在iOS7之后闽铐,蘋果自身提供了二維碼的掃描功能,從效率上來說奶浦,原生的二維碼遠高于這些第三方框架兄墅,現在就看看類似與微信掃一掃的界面的實現;

微信掃一掃

??實現掃描二維碼澳叉,首先要調起系統攝像頭創(chuàng)建視頻會話隙咸,所以需要導入AVFoundation框架;還要用到以下幾個類:

@property (strong,nonatomic)AVCaptureDevice *device;              // 捕捉硬件設備
@property (strong,nonatomic)AVCaptureDeviceInput *input;          // 外界成像輸入配置
@property (strong,nonatomic)AVCaptureMetadataOutput *output;      // 成像輸出配置
@property (strong,nonatomic)AVCaptureSession *session;            // 捕捉設置
@property (strong,nonatomic)AVCaptureVideoPreviewLayer *prelayer; // 圖層布局層

@property (nonatomic, retain) UIImageView *line;                  // 掃描動畫線

1成洗、AVCaptureDevice:捕捉硬件設備五督;
2、AVCaptureDeviceInput: 外界成像輸入配置瓶殃。這個類用來表示輸入數據的硬件設備充包,配置抽象設備的port;
3遥椿、AVCaptureMetadataOutput: 成像輸出配置基矮。這個支持二維碼、條形碼等圖像數據的識別冠场;
4家浇、AVCaptureSession: 捕捉設置,此類作為硬件設備輸入輸出信息的橋梁碴裙,承擔實時獲取設備數據的責任钢悲;
5、AVCaptureVideoPreviewLayer: 圖層類舔株。用來快速呈現攝像頭獲取的原始數據二維碼掃描功能的實現步驟是創(chuàng)建好會話對象莺琳,用來獲取從硬件設備輸入的數據,并實時顯示在界面上督笆;

下面是具體實現和創(chuàng)建方法芦昔,在創(chuàng)建前先檢測下攝像頭有沒有允許開啟:

- (void)setupCamera
{
    AVCaptureDevice *device = [AVCaptureDevice   defaultDeviceWithMediaType:AVMediaTypeVideo];
    if (device==nil){
        UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"溫馨提示" message:@"攝像頭未開啟" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles:nil, nil];
        [alert show];
        return;
    }
    // Device (獲取手機設備的硬件 —— 攝像頭)
    _device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
    // Input (獲取手機攝像頭的輸入流設置)
    _input = [AVCaptureDeviceInput deviceInputWithDevice:_device error:nil];
    
    // Output (獲取手機攝像頭的輸出流設置)
    _output = [[AVCaptureMetadataOutput alloc]init];
    // 設置輸出流協議 AVCaptureMetadataOutputObjectsDelegate
    [_output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    
    // Session 硬件配置設置
    _session = [[AVCaptureSession alloc]init];
    [_session setSessionPreset:AVCaptureSessionPresetHigh];
    // 加入硬件配置的輸入輸出流
    if ([_session canAddInput:self.input])
    {
        [_session addInput:self.input];
    }
    
    if ([_session canAddOutput:self.output])
    {
        [_session addOutput:self.output];
    }
    
    // 條碼類型 AVMetadataObjectTypeQRCode
    
    if ([self.output.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeQRCode])
    {
        self.output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code];
    }
    
    // Preview
    dispatch_async(dispatch_get_main_queue(), ^{
        // 更新界面
        _prelayer =[AVCaptureVideoPreviewLayer layerWithSession:self.session];
        _prelayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
        _prelayer.frame = CGRectMake(0, 64, SCREEN_WIDTH, SCREEN_HEIGHT-64);
        [self.view.layer insertSublayer:self.prelayer atIndex:0];
        // Start 開啟攝像頭運行
        [_session startRunning];
    });
    
}

設置攝像頭拍照的范圍就是_prelayer.frame這個方法,注意,這是拍照的范圍娃肿,并不是掃描的范圍咕缎,要想限制二維碼掃描區(qū)域珠十,讓用戶能夠準確對準某個二維碼,不讓整個屏幕都是掃描區(qū)域凭豪,像微信一樣設置區(qū)域在框內焙蹭,需要設置一個參數rectOfInterest ,這個參數有點特別嫂伞,這個參數的rect 與平常設置的坐標系是完全相反的孔厉,原點坐標在右上角,即X與Y互換帖努、W與H互換撰豺,這個屬性的每一個值取值范圍在0~1之間;先設置掃描區(qū)域的寬拼余、高以及屏幕寬污桦、高的宏定義:

#define SCREEN_WIDTH        [UIScreen mainScreen].bounds.size.width
#define SCREEN_HEIGHT       [UIScreen mainScreen].bounds.size.height

#define scanWidth           (SCREEN_WIDTH  - 120)
#define scanHeight          scanWidth
#define scanX               (SCREEN_WIDTH - scanWidth)/2
#define scanY               (SCREEN_HEIGHT - scanHeight)/2

給output設置掃描區(qū)域:

//設置掃描區(qū)域
CGFloat x = scanX/SCREEN_WIDTH;
CGFloat y = scanY/SCREEN_HEIGHT;
CGFloat width = scanWidth/SCREEN_WIDTH;
CGFloat height = scanHeight/SCREEN_HEIGHT;
//y 與 x 互換  width 與 height 互換
[_output setRectOfInterest:CGRectMake(y,x, height, width)];

到這,掃描已經準備完畢匙监,現在實現掃描動畫凡橱,使線循環(huán)上下運動;首先定義一個全局變量亭姥,掃描線和定時器:

@property(assign, nonatomic) BOOL                       isUp;
@property(strong, nonatomic) NSTimer                    *timer;

初始化圖片框和掃描線:

-(void)initView{
    
    _isUp = NO;
    
    UIImageView * imageView = [[UIImageView alloc]initWithFrame:CGRectMake(scanX, scanY, scanWidth, scanHeight)];
    imageView.image = [UIImage imageNamed:@"watch_sao_kuang"];
    [self.view addSubview:imageView];
    
    _line = [[UIImageView alloc] initWithFrame:CGRectMake(scanX, scanY+5, scanWidth, 2)];
    _line.image = [UIImage imageNamed:@"watch_sao_line"];
    [self.view addSubview:_line];
    
    _timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(repeatTime) userInfo:nil repeats:YES];
}

給定時器設置為0.01秒稼钩,線移動到上下間隔5個像素,實現動畫:

-(void)repeatTime
{

    CGRect rect = _line.frame;
    if (_isUp == NO) {
        
        rect.origin.y += 1;
        
        if (rect.origin.y >= scanY + scanHeight - 5 - rect.size.height)
        {
            _isUp = YES;
        }
    } else {
        rect.origin.y -= 1;
        
        if (rect.origin.y <= scanY + 5)
        {
            _isUp = NO;
        }
    }
    _line.frame = rect;
}

運行效果如下:

效果圖

??看看上面的微信掃一掃界面达罗,似乎還差一步坝撑,就是非掃描區(qū)域和掃描區(qū)域顏色有些區(qū)分,顏色暗一點氮块,有同學說這個簡單绍载,在非掃描區(qū)域覆蓋上、下滔蝉、左、右4個View就行塔沃,是的蝠引,可以這樣做,我第一次寫項目也是這么做的蛀柴,不難螃概,就是坐標算起來稍微復雜點,在這里介紹一種更為簡單的方法鸽疾,使用CAShapeLayer動畫吊洼,CAShapeLayer 是一個通過矢量圖形而不是 bitmap 來繪制的圖層子類≈瓢梗可以指定顏色冒窍、線寬等屬性递沪,用CGPath 來定義想要繪制的圖形,最后 CAShapeLayer 就會自動渲染出來综液。主要思路是設置繪制的路徑及顏色款慨,然后進行繪畫填充,代碼如下:

- (void)setshapeLayer{
    
    _shapeLayer = [[CAShapeLayer alloc] init];
    CGMutablePathRef path = CGPathCreateMutable();
    
    //添加蒙版
    CGPathAddRect(path, nil, self.view.bounds);
    
    CGPathAddRect(path, nil, CGRectMake(scanX, scanY, scanWidth, scanHeight));
    
    //填充規(guī)則
    _shapeLayer.fillRule = kCAFillRuleEvenOdd;
    //繪制的路徑
    _shapeLayer.path = path;
    //路徑中的填充顏色
    _shapeLayer.fillColor = [UIColor blackColor].CGColor;
    //設置透明度
    _shapeLayer.opacity = 0.5;
    
    
    [_shapeLayer setNeedsDisplay];
    
    [self.view.layer addSublayer:_shapeLayer];
}

有幾個關鍵屬性谬莹,就是添加蒙版CGPathAddRect,可以看到我們添加了兩次蒙版檩奠,為什么呢?因為除了中間的掃描區(qū)域之外附帽,其他的全部要置灰埠戳,所以要添加兩條路徑,一條是屏幕大小蕉扮,還有一條是掃描區(qū)域大小乞而。然后將路徑賦給CAShapeLayer的path屬性;想清楚這個規(guī)則慢显,這就要還得看看填充規(guī)則屬性fillRule爪模,它有兩個屬性:kCAFillRuleNonZerokCAFillRuleEvenOdd:

  • kCAFillRuleNonZero:默認值,非零規(guī)則荚藻,當這個點作任意方法的射線屋灌,然后看射線和路徑的交點方向,選擇一個作為基準方向应狱,如果方向一致則加1共郭,方向不一致則減1。為0時疾呻,點不在路徑內除嘹。
  • kCAFillRuleEvenOdd:奇偶規(guī)則,當這個點作任意方法的射線岸蜗,射線和路徑的交點數量是奇數則認為點在內部尉咕。

我們選擇的是kCAFillRuleEvenOdd奇偶規(guī)則,意思就是添加蒙版的地方隨意找個點畫射線,與蒙版路徑相交的點數是奇還是偶璃岳,奇數代表是內部年缎,偶數代表是外部,內部填充顏色铃慷,外部不管单芜。可能你還不是很理解犁柜,下面我畫了一張圖洲鸠,更加直觀:

kCAFillRuleEvenOdd規(guī)則

由圖可知,1馋缅、2代表大框除了小框的部分區(qū)域扒腕,就是我們的非掃描區(qū)域绢淀,看出無論從哪個方向畫射線,交點數都是基數個袜匿,黃點代表焦點數更啄,1有一個交點,2有三個交點居灯;小框內部部分祭务,就是我們的掃描區(qū)域,任意畫出的射線都是二個交點怪嫌,如線3义锥;所以CAShapeLayer自動識別,實現了中間的掃描區(qū)域之外岩灭,其他的全部要置灰拌倍;得到的效果圖如下:

最終效果圖

??所有的準備工作都做完了,現在是驗收階段了噪径,只要我們包含了<AVCaptureMetadataOutputObjectsDelegate>代理就可以通過代理方法獲取掃描到的東西了:

#pragma mark AVCaptureMetadataOutputObjectsDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    
    NSString *stringValue;
    
    // 元數據對象
    if ([metadataObjects count] >0)
    {
        // 可讀碼對象
        AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjects objectAtIndex:0];
        stringValue = metadataObject.stringValue;
        
        // 掃描成功后調用柱恤,解析二維碼
        [_timer setFireDate:[NSDate distantFuture]];
        [_session stopRunning];
        
        NSLog(@"掃一掃結果:%@",stringValue);
        
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"掃描結果" message:stringValue preferredStyle:UIAlertControllerStyleAlert];
        [alert addAction:[UIAlertAction actionWithTitle:@"確認" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
            if (_session != nil && _timer != nil) {
                [_session startRunning];
                [_timer setFireDate:[NSDate date]];
            }
            
        }]];
        [self presentViewController:alert animated:YES completion:nil];
    }

}

到這里掃描的頁面全部完成了。有幾點值得注意的地方找爱,當掃描成功之后梗顺,已經執(zhí)行了[_session stopRunning];所以當我進入下個頁面返回或者左滑又松開的時候頁面會卡死,想要重新掃描车摄,必須重新開啟寺谤,還有定時器也需要重新開啟,我們可以放在viewWillAppear方法開啟:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"掃一掃";
    self.view.backgroundColor = [UIColor whiteColor];
    
    [self initView];
    
    [self setshapeLayer];
    
    [self setupCamera];
    
}
-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    
    [_timer setFireDate:[NSDate date]];
    [_session startRunning];
    
}
-(void)viewDidDisappear:(BOOL)animated{
    [super viewDidDisappear:animated];
    
    [_session stopRunning];
}

聲明: 轉載請注明出處http://www.reibang.com/p/34c66cd69df9

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末吮播,一起剝皮案震驚了整個濱河市变屁,隨后出現的幾起案子,更是在濱河造成了極大的恐慌意狠,老刑警劉巖粟关,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異摄职,居然都是意外死亡誊役,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門谷市,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人击孩,你說我怎么就攤上這事迫悠。” “怎么了巩梢?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵创泄,是天一觀的道長艺玲。 經常有香客問我,道長鞠抑,這世上最難降的妖魔是什么饭聚? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮搁拙,結果婚禮上秒梳,老公的妹妹穿的比我還像新娘。我一直安慰自己箕速,他們只是感情好酪碘,可當我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盐茎,像睡著了一般兴垦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上字柠,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天探越,我揣著相機與錄音,去河邊找鬼窑业。 笑死钦幔,一個胖子當著我的面吹牛,可吹牛的內容都是我干的数冬。 我是一名探鬼主播节槐,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拐纱!你這毒婦竟也來了铜异?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤秸架,失蹤者是張志新(化名)和其女友劉穎揍庄,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體东抹,經...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡蚂子,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了缭黔。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片食茎。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖馏谨,靈堂內的尸體忽然破棺而出别渔,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布哎媚,位于F島的核電站喇伯,受9級特大地震影響,放射性物質發(fā)生泄漏拨与。R本人自食惡果不足惜稻据,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望买喧。 院中可真熱鬧捻悯,春花似錦、人聲如沸岗喉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽钱床。三九已至荚斯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間查牌,已是汗流浹背事期。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留纸颜,地道東北人兽泣。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像胁孙,于是被迫代替她去往敵國和親唠倦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內容

  • // 3. 添加輸出設備 pragma marke 掃描的代理方法 實現這個代理方法涮较,在這里面獲取掃描的結果...
    cj小牛閱讀 553評論 0 5
  • 關于二維碼(或者條形碼,以下歸類簡稱二維碼)掃描和生成的,我相信網絡上相關的文章層數不窮,但是,大部分都是直接粘貼...
    FR_Zhang閱讀 6,683評論 10 41
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理稠鼻,服務發(fā)現,斷路器狂票,智...
    卡卡羅2017閱讀 134,715評論 18 139
  • 簡介 二維條碼/二維碼是用某種特定的幾何圖形按一定規(guī)律在平面分布的黑白相間的圖形記錄數據符號信息的 在編碼上巧妙地...
    論丶道閱讀 2,527評論 3 7
  • 是的闺属,優(yōu)雅得形容別人很胖有很多種方法慌盯, 但是我們胖子也是有尊嚴的好嗎! 下次記得要反擊: .......... 經...
    自愈學院閱讀 346評論 0 2