iOS二維碼掃描與生成(優(yōu)化啟動卡頓)

相信很多小伙伴都做過二維碼的掃描生成,網(wǎng)上也有很多相關的Demo和博客從一開始的三方架構到后來用原生的都有,我之前寫過一篇關于二維碼的博客,不過之前使用swift寫的,可能現(xiàn)在已經不能看了(沒辦法,還要轉...)今天寫OC版的,著重說一下二維碼的啟動卡頓問題,之前一直沒有優(yōu)化,話不多說,先看效果圖吧

Untitled.gif

這個是仿照微信的啟動效果,不會卡頓,十分流暢,之前的一般是卡一下才會啟動的,現(xiàn)在是把加載過程帶入到下個界面,這樣就不會感覺到絲毫的卡頓,下面會給大家著重說這點的

好,我們開始簡單的說下掃描與生成的代碼吧,因為這個已經很多了,所以我這里就簡單的說下吧

1.二維碼生成

使用系統(tǒng)的就十分簡單,幾句代碼就搞定了,效果也十分可觀
首先使用CIFilter濾鏡生成CIImage,可以添加顏色濾鏡自定義背景顏色和二維碼顏色

/**
 7.生成CIImage
 
 - parameter size:    大小
 - parameter color:   顏色
 - parameter bgColor: 背景顏色
 
 - returns: CIImage
 */
-(CIImage*)generateCIImageWithSize:(CGFloat)size color:(UIColor*)color bgColor:(UIColor*)bgColor
{
    //設置缺省值
    CGFloat QRCodeSize = 300;//默認300
    UIColor* QRCodeColor = [UIColor blackColor];//默認黑色二維碼
    UIColor * QRCodeBgColor = [UIColor whiteColor];//默認白色背景
    
    //2.二維碼濾鏡
    NSData* contentData = [self dataUsingEncoding:NSUTF8StringEncoding];
    CIFilter *fileter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    [fileter setValue:contentData forKey:@"inputMessage"];
    [fileter setValue:@"H" forKey:@"inputCorrectionLevel"];
    CIImage *ciImage = fileter.outputImage;
    
    //3.顏色濾鏡
    CIFilter *colorFilter = [CIFilter filterWithName:@"CIFalseColor"];
    [colorFilter setValue:ciImage forKey:@"inputImage"];
    [colorFilter setValue:[CIColor colorWithCGColor:QRCodeColor.CGColor] forKey:@"inputColor0"];// 二維碼顏色
    [colorFilter setValue:[CIColor colorWithCGColor:QRCodeBgColor.CGColor] forKey:@"inputColor1"];// 背景色

    
    //4.生成處理
    CIImage*outImage = colorFilter.outputImage;
    CGFloat scale = QRCodeSize / outImage.extent.size.width;
    
    return [colorFilter.outputImage imageByApplyingTransform:CGAffineTransformMakeScale(scale, scale)];
}

然后就可以直接使用CIImage生成二維碼圖片,我們這里繪制了一下logo(仿微信)

/**
 6.生成二維碼
 
 - parameter size:            大小
 - parameter color:           顏色
 - parameter bgColor:         背景顏色
 - parameter logo:            圖標
 - parameter radius:          圓角
 - parameter borderLineWidth: 線寬
 - parameter borderLineColor: 線顏色
 - parameter boderWidth:      帶寬
 - parameter borderColor:     帶顏色
 
 - returns: 自定義二維碼
 */
-(UIImage*)generateQRCodeWithSize:(CGFloat)size
                            color:(UIColor*)color
                          bgColor:(UIColor*)bgColor
                             logo:(UIImage*)logo
                           radius:(CGFloat)radius
                  borderLineWidth:(CGFloat)borderLineWidth
                  borderLineColor:(UIColor*)borderLineColor
                       boderWidth:(CGFloat)boderWidth
                      borderColor:(UIColor*)borderColor
{
    CIImage* ciImage = [self generateCIImageWithSize:size color:color bgColor:bgColor];
    UIImage *image = [UIImage imageWithCIImage:ciImage];
    if (!logo) return image;
    if (!image) return nil;
    
    CGFloat logoWidth = image.size.width/4;
    CGRect logoFrame = CGRectMake((image.size.width - logoWidth) /  2,(image.size.width - logoWidth) / 2,logoWidth,logoWidth);
    
    // 繪制logo
    UIGraphicsBeginImageContextWithOptions(image.size, false, [UIScreen mainScreen].scale);
    [image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
    
    //線框
    UIImage*logoBorderLineImagae = [logo getRoundRectImageWithSize:logoWidth radius:radius borderWidth:borderLineWidth borderColor:borderLineColor];
    //邊框
      UIImage*logoBorderImagae = [logoBorderLineImagae getRoundRectImageWithSize:logoWidth radius:radius borderWidth:boderWidth borderColor:borderColor];
    
    [logoBorderImagae drawInRect:logoFrame];
    
    UIImage* QRCodeImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return QRCodeImage;
}

上面的代碼寫的比較多,主要是加了很多自定義的設置參數(shù),所以比較繁瑣,不過大家如果使用的話很簡單,我給出了很多api一些參數(shù)都已經使用default了,如果你想要微信的效果只要調用方法3傳入一個logo就行

/**
 1.生成二維碼
 
 - returns: 黑白普通二維碼(大小為300)
 */
-(UIImage*)generateQRCode;



/**
 2.生成二維碼
 
 - parameter size: 大小
 
 - returns: 生成帶大小參數(shù)的黑白普通二維碼
 */
-(UIImage*)generateQRCodeWithSize:(CGFloat)size;



/**
 3.生成二維碼
 
 - parameter logo: 圖標
 
 - returns: 生成帶Logo二維碼(大小:300)
 */
-(UIImage*)generateQRCodeWithLogo:(UIImage*)logo;



/**
 4.生成二維碼
 
 - parameter size: 大小
 - parameter logo: 圖標
 
 - returns: 生成大小和Logo的二維碼
 */
-(UIImage*)generateQRCodeWithSize:(CGFloat)size
                             logo:(UIImage*)logo;



/**
 5.生成二維碼
 
 - parameter size:    大小
 - parameter color:   顏色
 - parameter bgColor: 背景顏色
 - parameter logo:    圖標
 
 - returns: 帶Logo拆又、顏色二維碼
 */
-(UIImage*)generateQRCodeWithSize:(CGFloat)size
                            color:(UIColor*)color
                          bgColor:(UIColor*)bgColor
                             logo:(UIImage*)logo;



/**
 6.生成二維碼
 
 - parameter size:            大小
 - parameter color:           顏色
 - parameter bgColor:         背景顏色
 - parameter logo:            圖標
 - parameter radius:          圓角
 - parameter borderLineWidth: 線寬
 - parameter borderLineColor: 線顏色
 - parameter boderWidth:      帶寬
 - parameter borderColor:     帶顏色
 
 - returns: 自定義二維碼
 */
-(UIImage*)generateQRCodeWithSize:(CGFloat)size
                            color:(UIColor*)color
                          bgColor:(UIColor*)bgColor
                             logo:(UIImage*)logo
                           radius:(CGFloat)radius
                  borderLineWidth:(CGFloat)borderLineWidth
                  borderLineColor:(UIColor*)borderLineColor
                       boderWidth:(CGFloat)boderWidth
                      borderColor:(UIColor*)borderColor;

另外二維碼的生成是比較耗時的有可能會阻塞主線程,特別是配置比較低的設配會有點卡頓,有經驗的同學都知道像這種耗時(特別是繪制logo的二維碼的時候)的操作要放到子線程去的

  dispatch_async(dispatch_get_global_queue(0, 0), ^{
        UIImage *image = [@"大家好,我是炮炮兵!" generateQRCodeWithLogo:_headImageView.image];
        dispatch_async(dispatch_get_main_queue(), ^{
            _QRCodeImageView.image = image;
        });
    });

2.二維碼識別

識別圖片中的二維碼,也很簡單,幾句代碼

-(NSString*)scanCodeContent
{
    NSData *imageData = UIImagePNGRepresentation(self);
    CIImage *ciImage = [CIImage imageWithData:imageData];
    
    CIContext *context = [CIContext contextWithOptions:@{kCIContextUseSoftwareRenderer : @(false), kCIContextPriorityRequestLow : @(false)}];
    //創(chuàng)建探測器
    CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:context options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}];
    NSArray *features = [detector featuresInImage:ciImage];
    CIQRCodeFeature *feature = [features firstObject];
    
    return feature.messageString.length ? feature.messageString : @"未識別!";
}

這里說一下,當圖片中有2個二維碼時也是可以掃描出來的,他返回的是一個數(shù)組,如果有2個掃描結果就會返回2個的結果放到數(shù)組中,不過一般的需求都是顯示一個結果,所以很多博客中都是直接取了firstObject的(當然我這個也是,嘿嘿,有需求或者想試一下的小伙伴可以自行打個斷點找一個多二維碼的圖片試一下)

3.二維碼掃描

這里就貼一下代碼,輸入輸出會話什么的都已經寫爛了,我這里就不在說了

//初始化掃描二維碼
-(void)initScanCode
{
    
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    NSError *error = nil;
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
    
    if (error)
    {
        [self showMessage:@"攝像頭不可用" title:@"溫馨提示" andler:nil];
        return;
    }
    
    if ([device lockForConfiguration:nil])
    {
        //自動白平衡
        if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance])
        {
            [device setWhiteBalanceMode:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance];
        }
        //自動對焦
        if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus])
        {
            [device setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
        }
        //自動曝光
        if ([device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure])
        {
            [device setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
        }
        [device unlockForConfiguration];
    }
    self.captureMetadataOutput = [[AVCaptureMetadataOutput alloc] init];
    [self.captureMetadataOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    self.captureSession = [[AVCaptureSession alloc] init];
    [self.captureSession setSessionPreset:AVCaptureSessionPresetHigh];
    [self.self.captureSession canAddInput:input] ? [self.captureSession addInput:input] : nil;
    [self.captureSession canAddOutput:self.captureMetadataOutput] ? [self.captureSession addOutput:self.captureMetadataOutput] : nil;
    
    [self.captureMetadataOutput setMetadataObjectTypes:@[
                                                         AVMetadataObjectTypeQRCode,
                                                         AVMetadataObjectTypeCode39Code,
                                                         AVMetadataObjectTypeCode128Code,
                                                         AVMetadataObjectTypeCode39Mod43Code,
                                                         AVMetadataObjectTypeEAN13Code,
                                                         AVMetadataObjectTypeEAN8Code,
                                                         AVMetadataObjectTypeCode93Code
                                                         ]];
    
    self.captureVideoPreviewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
    self.captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    self.captureVideoPreviewLayer.frame = self.view.layer.bounds;
    [self.view.layer insertSublayer:self.captureVideoPreviewLayer atIndex:0];
    
    [self loadScan];
}

這里二維碼相關的設置設置完以后我們就要啟動掃描了,這里一般是直接調方法 [self.captureSession startRunning]來開始掃描,但問題就在這里,這個方法是會阻塞主線程的直到啟動完成,api說明里是有注明的,所以我們就不能這樣用,會照成卡頓,我們要放到子線程去啟動就行了,順便做一個加載動畫,跟微信的做法保持一致


//啟動掃描
-(void)loadScan
{
    [_loaddingIndicatorView startAnimating ];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self.captureSession startRunning];
        dispatch_async(dispatch_get_main_queue(), ^{
            [_loaddingIndicatorView stopAnimating];
            [UIView animateWithDuration:0.25 animations:^{
                _contentLabel.alpha = 1;
                _boxLayoutConstraint.constant = [UIScreen mainScreen].bounds.size.width*0.6;
                [self.view layoutIfNeeded];
            } completion:^(BOOL finished) {
                _scanLine.frame = CGRectMake(0 , 0, [UIScreen mainScreen].bounds.size.width*0.6, 3);
                [_scanLine.layer addAnimation:[self moveAnimation] forKey:nil];
                [self setScanReact:NO];
            }];
            self.captureMetadataOutput.rectOfInterest = [self.captureVideoPreviewLayer metadataOutputRectOfInterestForRect:_scanPane.frame];
        });
    });
}

這里啟動完成之后設置掃描設置掃描范圍的要說一下,很多博客和教程都是自己去設置掃描范圍的(那個生成范圍的轉換很坑爹,不好把控),系統(tǒng)提供的有把CGReact轉換成OutputReact的方法:
- (CGRect)metadataOutputRectOfInterestForRect:(CGRect)rectInLayerCoordinates
這里設置的時候要注意要等你的frame穩(wěn)定以后再設置,特別是使用AuotLayout的,不要一上來就設置這個,因為你的frame更新至少要在layoutSubviews那里才會更新的,我一般就放在掃描啟動完成以后才設置范圍

同時開始掃描和停止掃描也是放在子線程中


//開始掃描
- (void)startScan
{
    [_scanLine.layer addAnimation:[self moveAnimation] forKey:nil];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self.captureSession startRunning];
    });
}

//停止掃描
- (void)stopScan
{
    
    [_scanLine.layer removeAllAnimations];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if (self.captureSession.isRunning )
        {
            [self.captureSession stopRunning];
        }
    });
}

好了,就這些了,代碼稍微有點亂,小伙伴們請諒解

Demo在這里

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末儒旬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子帖族,更是在濱河造成了極大的恐慌栈源,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竖般,死亡現(xiàn)場離奇詭異甚垦,居然都是意外死亡,警方通過查閱死者的電腦和手機涣雕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門艰亮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人挣郭,你說我怎么就攤上這事迄埃。” “怎么了丈屹?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵调俘,是天一觀的道長。 經常有香客問我旺垒,道長彩库,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任先蒋,我火速辦了婚禮骇钦,結果婚禮上,老公的妹妹穿的比我還像新娘竞漾。我一直安慰自己眯搭,他們只是感情好,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布业岁。 她就那樣靜靜地躺著鳞仙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪笔时。 梳的紋絲不亂的頭發(fā)上棍好,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機與錄音,去河邊找鬼借笙。 笑死扒怖,一個胖子當著我的面吹牛,可吹牛的內容都是我干的业稼。 我是一名探鬼主播盗痒,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼低散!你這毒婦竟也來了俯邓?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤熔号,失蹤者是張志新(化名)和其女友劉穎看成,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跨嘉,經...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年吃嘿,在試婚紗的時候發(fā)現(xiàn)自己被綠了祠乃。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡兑燥,死狀恐怖亮瓷,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情降瞳,我是刑警寧澤嘱支,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站挣饥,受9級特大地震影響除师,放射性物質發(fā)生泄漏。R本人自食惡果不足惜扔枫,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一汛聚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧短荐,春花似錦倚舀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至糠排,卻和暖如春舵稠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工柱查, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留廓俭,地道東北人。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓唉工,卻偏偏與公主長得像研乒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子淋硝,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,744評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理雹熬,服務發(fā)現(xiàn),斷路器谣膳,智...
    卡卡羅2017閱讀 134,628評論 18 139
  • 科多大數(shù)據(jù)小課堂開課啦竿报,帶你認識Hadoop中Hbase的體系結構: HRegion 當一張表中的數(shù)據(jù)特別多的時候...
    大數(shù)據(jù)在說話閱讀 762評論 0 2
  • 周末又是下雨,下雨宅在家里是最佳的選擇诡壁,可是我這個女神經济瓢,在家宅到將近5點鐘以后,就覺得太悶了妹卿,要出去走走旺矾。最近沒...
    熊芳菲閱讀 230評論 0 0
  • 澎湃一下:天戴其蒼,地履其黃《峥耍縱有千古,橫有八荒箕宙。前途似海,來日方長。梁啟超說的話 來日方長懊直,好可以說壞可以說...
    一蓑煙雨任喵生閱讀 202評論 0 0