iOS 原生二維碼掃描(可限制掃描區(qū)域)

寫(xiě)這篇文章的主要原因不是展示如何使用 AVFoundation 來(lái)進(jìn)行二維碼掃描,更主要的是限制掃描二維碼的范圍。(因?yàn)槟J(rèn)的是全屏掃描)


項(xiàng)目遇到掃描二維碼的功能需求,這里我放棄了使用三方庫(kù),而采用了蘋(píng)果原生的掃描。
原生的好處就是掃描特別快效率特別高,但是遇到一個(gè)問(wèn)題就是不知道怎么去限制掃描范圍。

還是先簡(jiǎn)單說(shuō)一下怎么使用來(lái)進(jìn)行二維碼掃描吧。
首先是要用到的幾個(gè)類(lèi)

@property (strong,nonatomic)AVCaptureDevice * device;
@property (strong,nonatomic)AVCaptureDeviceInput * input;
@property (strong,nonatomic)AVCaptureMetadataOutput * output;
@property (strong,nonatomic)AVCaptureSession * session;
@property (strong,nonatomic)AVCaptureVideoPreviewLayer * preview;

他們之間的關(guān)系可以看下面的篇文章
傳送門(mén)

下面分別創(chuàng)建他們

// Device
 _device = [AVCaptureDevicedefaultDeviceWithMediaType:AVMediaTypeVideo];

// Input
_input = [AVCaptureDeviceInputdeviceInputWithDevice:self.deviceerror:nil];

// Output
_output = [[AVCaptureMetadataOutputalloc]init];
[_outputsetMetadataObjectsDelegate:selfqueue:dispatch_get_main_queue()];

// Session
_session = [[AVCaptureSessionalloc]init];
[_sessionsetSessionPreset:AVCaptureSessionPresetHigh];
      if ([_sessioncanAddInput:self.input])
{
    [_sessionaddInput:self.input];
}

      if ([_sessioncanAddOutput:self.output])
{
    [_sessionaddOutput:self.output];
}

// 條碼類(lèi)型 AVMetadataObjectTypeQRCode
_output.metadataObjectTypes =@[AVMetadataObjectTypeQRCode];

// Preview
_preview =[AVCaptureVideoPreviewLayerlayerWithSession:_session];
_preview.videoGravity =AVLayerVideoGravityResizeAspectFill;
_preview.frame =self.view.layer.bounds;
[self.view.layerinsertSublayer:_previewatIndex:0];

// Start
[_sessionstartRunning];

 然后實(shí)現(xiàn) AVCaptureMetadataOutputObjectsDelegate
#pragma mark AVCaptureMetadataOutputObjectsDelegate
  - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
 NSString *stringValue;
if ([metadataObjectscount] >0)
   {
       //停止掃描
       [_sessionstopRunning];        
       AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjectsobjectAtIndex:0];
       stringValue = metadataObject.stringValue;        
}

}

到此為止就可以成功掃描二維碼了,但是有個(gè)尷尬的問(wèn)題,這時(shí)的掃描是全屏掃描的。即


Screenshot_2016-08-26-13-47-34_com.tencent.mm.png

一般情況下項(xiàng)目中的掃描頁(yè)面是這樣的,但是當(dāng)你掃描的時(shí)候會(huì)發(fā)現(xiàn)在二維碼還沒(méi)進(jìn)入中心的那個(gè)小方塊時(shí),就已經(jīng)成功掃描完成了,這對(duì)于體驗(yàn)來(lái)說(shuō)很不好。但是由于那時(shí)候趕項(xiàng)目就沒(méi)有時(shí)間優(yōu)化。終于今天抽出來(lái)時(shí)間了。
我從早上上班開(kāi)始一直搞到下午,把所有想到的方法都試了一遍,但是都不行(都是淚),最后將要放棄的時(shí)候發(fā)現(xiàn)了一個(gè)比較可疑的點(diǎn)。

@property(nonatomic)CGRect rectOfInterest NS_AVAILABLE_IOS(7_0);

這是的 AVCaptureMetadataOutput 一個(gè)屬性,它的解釋是
@discussion
The value of this property is a CGRect that determines the receiver's rectangle of interest for each frame of video.
The rectangle's origin is top left and is relative to the coordinate space of the device providing the metadata. Specifying
a rectOfInterest may improve detection performance for certain types of metadata. The default value of this property is the
value CGRectMake(0, 0, 1, 1). Metadata objects whose bounds do not intersect with the rectOfInterest will not be returned.

大概意思就是設(shè)置每一幀畫(huà)面感興趣的區(qū)域(字面意思),那豈不是就是設(shè)置掃描范圍嘍,大喜
于是趕緊把rectOfInterest設(shè)置成中間框的frame

[_outputsetRectOfInterest:CGRectMake((ScreenWidth-220)/2,60+64,220, 220)];

//中間區(qū)域的寬和高都是220 ScreenWidth為設(shè)備屏幕寬度
但是卻發(fā)現(xiàn)怎么掃描都不能成功了。于是又看了看上面的一段話(huà)。

第二句:區(qū)域的原點(diǎn)在左上方(后面才知道坑苦我了!),然后區(qū)域是相對(duì)于設(shè)備的大小的,默認(rèn)值是CGRectMake(0, 0, 1, 1),這時(shí)候我才知道是有比例關(guān)系的,最大值才是1,也就是說(shuō)只要除以相應(yīng)的設(shè)備寬和高的大小不就行了?然后就改成

[_outputsetRectOfInterest:CGRectMake(((ScreenWidth-220)/2)/ScreenWidth,(60+64)/ScreenHigh,220/ScreenWidth,220/ScreenHigh)];

按說(shuō)這樣應(yīng)該就完美了,但是才知道我還是高興得太早了,一掃描才發(fā)現(xiàn)完全不是那么回事,差很多。
于是我就一點(diǎn)一點(diǎn)調(diào),但是最后也沒(méi)調(diào)成功,最后一狠心有設(shè)置了一個(gè)很確定的值。

[_output setRectOfInterest:CGRectMake(0.5,0.5,0.5, 0.5)];

這次應(yīng)該很確定是在右下方的四分之一區(qū)域吧,嘿嘿。
但是事實(shí)又一次打擊了我,掃描后發(fā)現(xiàn)是左下的四分之一區(qū)域,也就是說(shuō)rectOfInterest的原點(diǎn)是右上角?。?!
回頭又一想,即使右上角是原點(diǎn)那也應(yīng)該沒(méi)有影響啊,但是為什么不行呢,不會(huì)是原點(diǎn)的 X 和 Y 互換了吧?算了不管怎么著,試一試吧。

[_outputsetRectOfInterest:CGRectMake((60+64)/ScreenHigh,((ScreenWidth-220)/2)/ScreenWidth,220/ScreenWidth,220/ScreenHigh)];

又掃描了一下發(fā)現(xiàn)成功了!果然原點(diǎn)正確了,我只想說(shuō)TMD!
但是寬和高又怎么對(duì)不上了?不會(huì)也互換了吧!趕緊試試

[_outputsetRectOfInterest:CGRectMake((124)/ScreenHigh,((ScreenWidth-220)/2)/ScreenWidth,220/ScreenHigh,220/ScreenWidth)];

懷著忐忑的心情又試了試,完美掃描!OMG我想死的心都有了。
于是用系統(tǒng)原生的掃描二維碼就完美了!

原文

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容