自用-二維碼掃描模塊代碼
效果圖
直接上代碼 注釋寫的還算詳細
-需要準備下面的東西,其中preView是要顯示的樣式(就是攝像頭畫面加一個掃描框,因為掃描是不需要畫面的,為了用戶體驗)
@interface ScanViewController () <AVCaptureMetadataOutputObjectsDelegate>
//1.輸入設備(從外界采集信息) 輸入設備很多種 攝像頭 麥克風 鍵盤
@property (nonatomic, strong) AVCaptureDeviceInput* input;
//2.輸出設備 (解析采集來得內容 然后獲取到數(shù)據(jù)) Metadata 元數(shù)據(jù)
@property (nonatomic, strong) AVCaptureMetadataOutput* output;
//3.會話 session (連接輸入和輸出進行工作)
@property (nonatomic, strong) AVCaptureSession* session;//4.展示layer@property (nonatomic, strong) PreView* preView;
@end
-
接下來是掃描代碼
#pragma mark--開始掃描
- (void)startScan
{//1.輸入設備(從外界采集信息) //創(chuàng)建具體的設備 攝像頭 //AVMediaTypeVideo 攝像頭 AVMediaTypeAudio 麥克風 AVCaptureDevice* device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; self.input = [AVCaptureDeviceInput deviceInputWithDevice:device error:NULL]; //2.輸出設備 (解析采集來得內容 然后獲取到數(shù)據(jù)) Metadata 元數(shù)據(jù) self.output = [[AVCaptureMetadataOutput alloc] init]; //3.會話 session (連接輸入和輸出進行工作) self.session = [[AVCaptureSession alloc] init]; //會話掃描展示的大小 [self.session setSessionPreset:AVCaptureSessionPresetHigh]; //添加輸入設備和輸出設備 if ([self.session canAddInput:self.input]) { [self.session addInput:self.input]; } if ([self.session canAddOutput:self.output]) { [self.session addOutput:self.output]; } //指定輸出設備的 代理 來返回 解析到得數(shù)據(jù) [self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; //設置元數(shù)據(jù)類型 QRCode 二維碼+條形碼 [self.output setMetadataObjectTypes:@[ AVMetadataObjectTypeQRCode, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code ]]; //.創(chuàng)建特殊視圖展示二維碼界面 self.preView = [[PreView alloc] initWithFrame:self.view.bounds]; self.preView.session = self.session; [self.view addSubview:self.preView]; //設置掃描區(qū)域(我設置的中間靠上w:300 h:300,這樣不會打開就掃描上,用戶體驗會好點)-需要先設置frame 然后轉化添加到上去 CGRect rect = CGRectMake(30, 100, 300, 300); CGRect interRect = [self.preView.previewLayer metadataOutputRectOfInterestForRect:rect]; self.output.rectOfInterest = interRect; //5.開啟 會話 [self.session startRunning]; }
然后就是掃描到結果 從代理<AVCaptureMetadataOutputObjectsDelegate>的實現(xiàn)中拿到結果
#pragma mark--掃描成功 二維碼代理--
/**
* 解析出元數(shù)據(jù)就會調用
*
* @param captureOutput 輸出
* @param metadataObjects 元數(shù)據(jù)數(shù)組
* @param connection 連接
/
- (void)captureOutput(AVCaptureOutput)captureOutput didOutputMetadataObjects:(NSArray)metadataObjects fromConnection:(AVCaptureConnection)connection
{
//結束掃描
[self stopScan];
NSMutableString* str = [NSMutableString string];
for (AVMetadataMachineReadableCodeObject* objc in metadataObjects) {
[str appendString:objc.stringValue];
}
//打印
NSLog(@"%@",(NSString *)str);
[self.navigationController popViewControllerAnimated:YES];
}
-結束掃描的代碼
#pragma mark--結束掃描
- (void)stopScan
{
//停止會話
[self.session stopRunning];
//移除特殊的圖層
[self.preView removeFromSuperview];
}-
接下來需要注意的就是preView的實現(xiàn) 向微信掃描一樣,有一個半透明的遮罩,中間有一個透明區(qū)域,這個地方用layer的mask實現(xiàn),掃描動畫,使用定時器實現(xiàn),上代碼
@interface PreView ()
@property(nonatomic,strong)UIImageView bgImage;
//添加線
@property (nonatomic, strong) UIImageView lineImageView;
//定時器
@property (nonatomic, strong) CADisplayLink* displayLink;
@end@implementation PreView /** * layer * @return 返回AVCaptureVideoPreviewLayer 特殊的layer 可以展示輸入設備采集到的信息 */ +(Class)layerClass{ return [AVCaptureVideoPreviewLayer class]; } -(AVCaptureVideoPreviewLayer *)previewLayer{ AVCaptureVideoPreviewLayer *layer = (AVCaptureVideoPreviewLayer *)self.layer; return layer; } -(void)setSession:(AVCaptureSession *)session{ _session = session; AVCaptureVideoPreviewLayer *layer = (AVCaptureVideoPreviewLayer *)self.layer; layer.session = session; } - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self setMask]; } return self; } -(void)setMask{ //添加掃描線 self.lineImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"line"]]; self.lineImageView.frame = CGRectMake((kScreenWidth - 280) / 2, 100, 280, 2); [self addSubview:self.lineImageView]; //計時器 self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(scanAnimation)]; [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; //創(chuàng)建一個maskView UIView *maskView = [[UIView alloc]initWithFrame:CGRectMake(0, 64, kScreenWidth, kScreenHigh-64)]; //顏色設置透明 maskView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5]; [self addSubview:maskView]; //創(chuàng)建一個中間有鏤空區(qū)域的path UIBezierPath *maskPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, kScreenWidth, kScreenHigh-64)]; [maskPath appendPath:[[UIBezierPath bezierPathWithRoundedRect:CGRectMake((kScreenWidth-280)/2, 100-64, 280, 280) cornerRadius:1] bezierPathByReversingPath]]; CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init]; maskLayer.path = maskPath.CGPath; maskView.layer.mask = maskLayer; //添加掃描框 [self addSubview:self.bgImage]; _bgImage.frame = CGRectMake((kScreenWidth-280)/2, 100, 280, 280); } #pragma mark--掃描動畫-- - (void)scanAnimation { CGRect frame = self.lineImageView.frame; if (self.lineImageView.frame.origin.y > 100 + 270) { frame.origin.y = 100; self.lineImageView.frame = frame; } else { frame.origin.y += 2; self.lineImageView.frame = frame; } } #pragma mark --懶加載 -(UIImageView *)bgImage{ if (!_bgImage) { _bgImage = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"icon_scanbg"]]; } return _bgImage; } @end
差不多 這樣就搞定了,需要注意的是preView的加載和移除 還有掃描方法的加載時間,發(fā)現(xiàn)如果Push出來的窗口,二維碼界面打開會有一秒多延遲,建議加一個MBProgress過度,如果Model的話,就沒有這個問題(執(zhí)行掃描我在viewWillAppear中加載)
附上github地址:https://github.com/superHS/Demo.git