之前的文章是在ViewController里實現(xiàn)了原生二維碼掃描的功能揍拆,后來覺得VC太重了厂财,便把功能抽成了一個View新症,在抽象的過程中也仔細(xì)捉摸了一些沒有仔細(xì)思考過的問題筑凫,特此記錄下來嫌变。
1.掃描區(qū)域的掃描線動畫
像微信等很多二維碼掃描界面吨艇,總有一個上下移動的掃描線動畫。一開始用我用NSTimer配合view的frame改變實現(xiàn)腾啥,后來發(fā)現(xiàn)不僅容易導(dǎo)致NSTimer的循環(huán)引用东涡,外界調(diào)用也容易開啟多個定時器,已經(jīng)在某些時候動畫會卡頓倘待。便換成了用CABasicAnimation實現(xiàn)疮跑,通過改變view的y值,并把設(shè)置repeatCount為最大凸舵,達(dá)到掃描線上下動的效果祖娘。
- (void)addScanLineAnimation{
self.scanLine.hidden = NO;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
animation.fromValue = @(0);
animation.toValue = @(self.scanRect.size.height - scanLineWidth);
animation.duration = scanTime;
animation.repeatCount = OPEN_MAX;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[self.scanLine.layer addAnimation:animation forKey:scanLineAnimationName];
}
- (void)removeScanLineAnimation{
[self.scanLine.layer removeAnimationForKey:scanLineAnimationName];
self.scanLine.hidden = YES;
}
取消時把動畫移除即可。
2.掃描線的漸變色拖尾
這個用CAGradientLayer實現(xiàn)即可啊奄,設(shè)置漸變起始點(diǎn)和終止點(diǎn)渐苏,傳入顏色數(shù)組和對應(yīng)的起始位置(0~1),即可菇夸。
- (UIView *)scanLine{
if (!_scanLine) {
_scanLine = [[UIView alloc]initWithFrame:CGRectMake(self.scanRect.origin.x,self.scanRect.origin.y, self.scanRect.size.width, scanLineWidth)];
_scanLine.hidden = YES;
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.startPoint = CGPointMake(0.5, 0);
gradient.endPoint = CGPointMake(0.5, 1);
gradient.frame = _scanLine.layer.bounds;
gradient.colors = @[(__bridge id)[self.scanLineColor colorWithAlphaComponent:0].CGColor,(__bridge id)[self.scanLineColor colorWithAlphaComponent:0.4f].CGColor,(__bridge id)self.scanLineColor.CGColor];
gradient.locations = @[@0,@0.96,@0.97];
[_scanLine.layer addSublayer:gradient];
}
return _scanLine;
}
3.設(shè)置AVFoundation原生二維碼類的一些容易崩潰的地方
具體x相關(guān)類別在上篇文章已經(jīng)有所介紹琼富,講講一些容易崩潰的點(diǎn),崩潰主要集中在設(shè)置AVCaptureMetadataOutput的metadaObjectTypes屬性時峻仇,所以在設(shè)置這個數(shù)組時公黑,最好先檢查數(shù)組里的值是否在其availableMetaDataObjectTypes屬性里。像這樣
if (![self.dataOutput.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeQRCode]) {
NSLog(@"The camera unsupport for QRCode.");
}
self.dataOutput.metadataObjectTypes = @[AVMetadataObjectTypeQRCode];
而一般的崩潰是因為availableMetaDataObjectTypes為空摄咆,而造成空的原因是AVCaptureSession沒有addInput和addOutput造成凡蚜。其中input有一個需要注意的地方,在setSessionPreset吭从,也就是取樣的視頻分辨率的時候朝蜘,如果設(shè)置了一個攝像頭不支持的分辨率,那么input是canAddInput會返回NO涩金。并不是所有設(shè)備都支持1080p視頻分辨率的谱醇,所以我們在setSessionPreset時暇仲,可以這樣
if ([self.device supportsAVCaptureSessionPreset:AVCaptureSessionPreset1920x1080]) {
[self.session setSessionPreset:AVCaptureSessionPreset1920x1080];
}
else{
[self.session setSessionPreset:AVCaptureSessionPresetHigh];
}
AVCaptureSessionPresetHigh就是該設(shè)備所支持最高的分辨率。而單純通過設(shè)備ScreenHigh來區(qū)分是否支持1080p視頻分辨率并不可靠副渴,4s以后是支持1080p的奈附,但是還有一些touch設(shè)備,iPad設(shè)備煮剧,已經(jīng)某些后攝像頭壞掉的手機(jī)(會自動調(diào)用前攝像頭斥滤,比如我的5sTAT)上,就會出現(xiàn)分辨率不支持勉盅,canAddInput返回NO佑颇,沒有input,availavleMetadataObjectTypes為空草娜,如果不注意就會產(chǎn)生崩潰挑胸。
4.關(guān)于掃描區(qū)域的正確計算方法
之前提到掃描區(qū)域的interestedRect是一個值是01的CGRect,后來我實驗發(fā)現(xiàn)宰闰,這個01相對的并不是屏幕的Frame茬贵,而是AVCaptureVideoPreviewLayer的Frame值,而我們習(xí)慣把AVCaptureVideoPreviewLayer的frame值等于view的frame值议蟆,從而才在計算時用view的frame值去算百分比闷沥。但后來我發(fā)現(xiàn)正確的轉(zhuǎn)換方法是用AVCaptureVideoPreviewLayer里的metadataOutputRectOfInterestForRect進(jìn)行正常Rect->掃描區(qū)域坐標(biāo)系Rect轉(zhuǎn)換,另外AVCaptureVideoPreviewLayer還提供了rectForMetadataOutputRectOfInterest進(jìn)行掃描區(qū)域Rect->正常坐標(biāo)系Rect的轉(zhuǎn)換咐容,以及CGPoint的相關(guān)轉(zhuǎn)換方法。而且這個轉(zhuǎn)換需要放在session的StartRunning方法之后才會生效蚂维。
[self.session startRunning];
self.dataOutput.rectOfInterest = [self.previewLayer metadataOutputRectOfInterestForRect:self.scanRect];
5.耗時操作放在異步并發(fā)線程中處理
AVFoundation里設(shè)置相機(jī)相關(guān)的方法比較耗時戳粒,可以放在異步并發(fā)線程中處理。
最后
個人封裝了一個CDZQRScanView簡單易用虫啥。支持Cocoapods也可以直接把兩個文件拉進(jìn)項目里蔚约。
所有源碼和Demo
使用時可以把VC兩個文件拖進(jìn)項目里就可。
如果您覺得有幫助,不妨給個star鼓勵一下,歡迎關(guān)注&交流
有任何問題歡迎評論私信或者提issue
QQ:757765420
Email:nemocdz@gmail.com
Github:Nemocdz
微博:@Nemocdz