1. 程式人生 > >iOS原生實現二維碼拉近放大

iOS原生實現二維碼拉近放大

http://www.cocoachina.com/ios/20180416/23033.html

2018-04-16 15:34 編輯: yyuuzhu 分類:iOS開發 來源:程式鵝 3008   Work Hard, Play Hard, Live Life.

前言

生活中,我們都是使用支付寶支付,當我們再掃描一個較遠的二維碼過程中,我們會發現,鏡頭會自動放大很容易掃到二維碼進行支付。看起來這麼人性化的操作,又是什麼原理,該怎麼實現呢?掃碼現在很常見, 很多App基本都具備掃碼功能, 網上也有很多對iOS二維碼的講解, Github上也有很多事例、開源的程式碼,但是發現APP掃碼功能上,自動拉近掃描二維碼的這波神操作,很少涉及。本文簡單介紹如何iOS原生如何實現掃描較小二維碼過程中拉近放大。
先看看本文效果圖:


  Demo效果圖

對比支付寶掃碼放大效果圖:


  支付寶

實現

從網路技術部落格、Github上,我們都能很快實現一個二維碼掃描功能,本文不再重複這些知識點。重點是如何拉近鏡頭和定位到二維碼判斷二維碼大小。

iOS原生掃碼

以下是iOS AVFoundation的掃碼原理圖。


  原理圖

拉近鏡頭

蘋果提供了AVCaptureConnection中,videoScaleAndCropFactor:縮放裁剪係數,使用該屬性,可以實現拉近拉遠鏡頭。

- (void)setVideoScale:(CGFloat)scale{ //注意改變裝置屬性前一定要首先呼叫lockForConfiguration:呼叫完之後使用unlockForConfiguration方法解鎖 [_input.device lockForConfiguration:nil]; //獲取放大最大倍數 AVCaptureConnection *videoConnection = [self connectionWithMediaType:AVMediaTypeVideo fromConnections:[[self stillImageOutput] connections]]; CGFloat maxScaleAndCropFactor = ([[self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo] videoMaxScaleAndCropFactor])/16; if (scale > maxScaleAndCropFactor)
        scale = maxScaleAndCropFactor; CGFloat zoom = scale / videoConnection.videoScaleAndCropFactor;
    
    videoConnection.videoScaleAndCropFactor = scale;
    
    [_input.device unlockForConfiguration]; CGAffineTransform transform = _videoPreView.transform;
    [CATransaction begin];
    [CATransaction setAnimationDuration:.025];
    
     _videoPreView.transform = CGAffineTransformScale(transform, zoom, zoom);
    
    [CATransaction commit];
   
}

實現步驟:
1、首先呼叫lockForConfiguration。
2、獲取系統相機最大倍數,根據需求自定義MAX倍數。
3、改變videoScaleAndCropFactor。
4、unlockForConfiguration方法解鎖。
5、將檢視layer層放大對應的倍數。

1、注意改變裝置屬性前一定要首先呼叫lockForConfiguration:呼叫完之後使用unlockForConfiguration方法解鎖。
2、the videoScaleAndCropFactor property may be set to a value in the range of 1.0 to videoMaxScaleAndCropFactor,videoScaleAndCropFactor這個屬性取值範圍是1.0-videoMaxScaleAndCropFactor,如果你設定超出範圍會崩潰哦


二維碼定位

我們都知道,原生掃描結果AVCaptureMetadataOutputObjectsDelegate是返回了一個數組,而數組裡面是一個個的AVMetadataMachineReadableCodeObject,而AVMetadataMachineReadableCodeObject中有個corners陣列,記錄二維碼的座標。查閱了官方文件和相關資料,我們很容易聯想到,通過corners來獲取二維碼的座標,大小形狀。

@@property corners
@abstract The points defining the (X,Y)
locations of the corners of the machine-readable code.

@discussion
The value of this property is an NSArray of
NSDictionaries, each of which has been created from a CGPoint using
CGPointCreateDictionaryRepresentation(), representing the coordinates
of the corners of the object with respect to the image in which it
resides. If the metadata originates from video, the points may be
expressed as scalar values from 0. - 1. The points in the corners
differ from the bounds rectangle in that bounds is axis-aligned to
orientation of the captured image, and the values of the corners
reside within the bounds rectangle. The points are arranged in
counter-clockwise order (clockwise if the code or image is mirrored),
starting with the top-left of the code in its canonical orientation.

#pragma mark AVCaptureMetadataOutputObjectsDelegate - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{ if (!bNeedScanResult) { return;
    }
    
    bNeedScanResult = NO; if (!_arrayResult) { self.arrayResult = [NSMutableArray arrayWithCapacity:1];
    } else {
        [_arrayResult removeAllObjects];
    } //識別掃碼型別 for(AVMetadataObject *current in metadataObjects)
    { if ([current isKindOfClass:[AVMetadataMachineReadableCodeObject class]] )
        {
            bNeedScanResult = NO; NSString *scannedResult = [(AVMetadataMachineReadableCodeObject *) current stringValue]; if (scannedResult && ![scannedResult isEqualToString:@""])
            {
                [_arrayResult addObject:scannedResult];
            } //測試可以同時識別多個二維碼 }
    } if (_arrayResult.count < 1)
    {
        bNeedScanResult = YES; return;
    } if (_isAutoVideoZoom && !bHadAutoVideoZoom) { AVMetadataMachineReadableCodeObject *obj = (AVMetadataMachineReadableCodeObject *)[self.preview transformedMetadataObjectForMetadataObject:metadataObjects.lastObject];
        [self changeVideoScale:obj];
         bNeedScanResult = YES;
         bHadAutoVideoZoom  =YES; return;
    } if (_isNeedCaputureImage)
    {
        [self captureImage];
    } else {
        [self stopScan]; if (_blockScanResult) {
            _blockScanResult(_arrayResult);
        }
    }
}

當使用iOS原生掃碼過程中,當系統相機檢測到二維碼型別,回撥通知我們,第一次我們可以檢測二維碼大小及其位置(如果非原生掃碼,可以通過OpenCV識別二維碼,將其定位,有待研究)。

- (void)changeVideoScale:(AVMetadataMachineReadableCodeObject *)objc
{ NSArray *array = objc.corners; CGPoint point = CGPointZero; int index = 0; CFDictionaryRef dict = (__bridge CFDictionaryRef)(array[index++]); // 把點轉換為不可變字典 // 把字典轉換為點,存在point裡,成功返回true 其他false CGPointMakeWithDictionaryRepresentation(dict, &point); NSLog(@"X:%f -- Y:%f",point.x,point.y); CGPoint point2 = CGPointZero; CGPointMakeWithDictionaryRepresentation((__bridge CFDictionaryRef)array[2], &point2); NSLog(@"X:%f -- Y:%f",point2.x,point2.y); CGFloat scace =150/(point2.x-point.x); //當二維碼圖片寬小於150,進行放大 if (scace > 1) { //實現動畫效果 for (CGFloat i= 1.0; i<=scace; i = i+0.001) {
            [self setVideoScale:i];
        }
    } return;
}

上程式碼塊中changeVideoScale函式已將二維碼位置和寬定位出來,根據需求當相機識別的二維碼的寬少於150時,我們可以通過拉近鏡頭來實現放大二維碼。

原始碼

上述是主要流程,完整的原始碼可以通過以下方式獲取
1、 CocoaPods安裝

pod 'RHScan'

2、RHScan

小結

本文使用iOS原生掃碼實現如何掃碼過程拉近鏡頭放大二維碼,上述方法並無提高識別的速率,反而減慢了,如果要提高識別速度,可以從OpenCV識別二維碼定位的方向考慮。如果你有更好的方法或方案麻煩告知。

iOS 模仿微信掃描二維碼放大功能
iOS 掃描二維碼實現手勢拉近拉遠鏡頭
iOS 識別過程中描繪二維碼邊框
RHScan

作者:程式鵝
連結:https://www.jianshu.com/p/080814eff67e