#import <UIKit/UIKit.h>
@protocol QRReaderViewControllerDelegate;
@interface QRReaderViewController : UIViewController
@property (nonatomic, assign) id<QRReaderViewControllerDelegate> delegate;
@end
@protocol QRReaderViewControllerDelegate <NSObject>
- (void)didFinishedReadingQR:(NSString *)string;
@end
#import "QRReaderViewController.h"
#import <AVFoundation/AVFoundation.h>
#import <objc/runtime.h>
@interface QRReaderViewController() <AVCaptureMetadataOutputObjectsDelegate>
@property (strong,nonatomic)AVCaptureSession *session;
@property (strong,nonatomic)AVCaptureVideoPreviewLayer *previewLayer;
@property (strong, nonatomic) UILabel *infoLabel;
@property (nonatomic, strong) UIImageView *scanLineImageView;
@property (nonatomic, strong) NSTimer *scanLineTimer;
@end
@implementation QRReaderViewController
- (void)initViewAndSubViews
{
CGRect mainBounds = [[UIScreen mainScreen] bounds];
self.view.frame = mainBounds;
CGRect readerFrame = self.view.frame;
CGSize viewFinderSize = CGSizeMake(readerFrame.size.width - 80, readerFrame.size.width - 80);
/**********************************摄像头开始**********************************/
// 1 实例化摄像头设备
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// An AVCaptureDevice object abstracts a physical capture device that provides input data (such as audio or video) to an AVCaptureSession object.
// 2 设置输入,把摄像头作为输入设备
// 因为模拟器是没有摄像头的,因此在此最好做个判断
NSError *error = nil;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (error)
{
NSLog(@"没有摄像头%@", error.localizedDescription);
return;
}
// 3 设置输出(Metadata元数据)
AVCaptureMetadataOutput *outPut = [[AVCaptureMetadataOutput alloc] init];
CGRect scanCrop =
CGRectMake((readerFrame.size.width - viewFinderSize.width)/2,
(readerFrame.size.height - viewFinderSize.height)/2,
viewFinderSize.width,
viewFinderSize.height);
outPut.rectOfInterest =
CGRectMake(scanCrop.origin.y/readerFrame.size.height,
scanCrop.origin.x/readerFrame.size.width,
scanCrop.size.height/readerFrame.size.height,
scanCrop.size.width/readerFrame.size.width
);
// 3.1 设置输出的代理
// 使用主线程队列,相应比较同步,使用其他队列,相应不同步,容易让用户产生不好的体验。
[outPut setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
// 4 拍摄会话
AVCaptureSession *session = [[AVCaptureSession alloc]init];
session.sessionPreset = AVCaptureSessionPreset640x480;
// 添加session的输入和输出
[session addInput:input];
[session addOutput:outPut];
// 4.1 设置输出的格式
[outPut setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
// 5 设置预览图层(用来让用户能够看到扫描情况)
AVCaptureVideoPreviewLayer *preview = [AVCaptureVideoPreviewLayer layerWithSession:session];
// AVCaptureVideoPreviewLayer -- to show the user what a camera is recording
// 5.1 设置preview图层的属性
[preview setVideoGravity:AVLayerVideoGravityResizeAspectFill];
// 5.2设置preview图层的大小
[preview setFrame:self.view.bounds];
//5.3将图层添加到视图的图层
[self.view.layer insertSublayer:preview atIndex:0];
self.previewLayer = preview;
self.session = session;
/**********************************摄像头结束**********************************/
/* 画一个取景框开始 */
// 正方形取景框的边长
CGFloat edgeLength = 20.0;
UIImageView *topLeft =
[[UIImageView alloc] initWithFrame:CGRectMake((readerFrame.size.width - viewFinderSize.width)/2,
(readerFrame.size.height - viewFinderSize.height)/2,
edgeLength, edgeLength)];
topLeft.image = [UIImage imageNamed:@"qr_top_left.png"];
[self.view addSubview:topLeft];
UIImageView *topRight =
[[UIImageView alloc] initWithFrame:CGRectMake((readerFrame.size.width + viewFinderSize.width)/2 - edgeLength,
(readerFrame.size.height - viewFinderSize.height)/2,
edgeLength, edgeLength)];
topRight.image = [UIImage imageNamed:@"qr_top_right.png"];
[self.view addSubview:topRight];
UIImageView *bottomLeft =
[[UIImageView alloc] initWithFrame:CGRectMake((readerFrame.size.width - viewFinderSize.width)/2,
(readerFrame.size.height + viewFinderSize.height)/2 - edgeLength,
edgeLength, edgeLength)];
bottomLeft.image = [UIImage imageNamed:@"qr_bottom_left"];
[self.view addSubview:bottomLeft];
UIImageView *bottomRight =
[[UIImageView alloc] initWithFrame:CGRectMake((readerFrame.size.width + viewFinderSize.width)/2 - edgeLength,
(readerFrame.size.height + viewFinderSize.height)/2 - edgeLength,
edgeLength, edgeLength)];
bottomRight.image = [UIImage imageNamed:@"qr_bottom_right"];
[self.view addSubview:bottomRight];
UIView *topLine =
[[UIView alloc] initWithFrame:CGRectMake((readerFrame.size.width - viewFinderSize.width)/2-1,
(readerFrame.size.height - viewFinderSize.height)/2-1,
viewFinderSize.width+2, 1)];
topLine.backgroundColor = [UIColor grayColor];
[self.view addSubview:topLine];
UIView *bottomLine =
[[UIView alloc] initWithFrame:CGRectMake((readerFrame.size.width - viewFinderSize.width)/2-1,
(readerFrame.size.height + viewFinderSize.height)/2,
viewFinderSize.width+2, 1)];
bottomLine.backgroundColor = [UIColor grayColor];
[self.view addSubview:bottomLine];
UIView *leftLine =
[[UIView alloc] initWithFrame:CGRectMake((readerFrame.size.width - viewFinderSize.width)/2-1,
(readerFrame.size.height - viewFinderSize.height)/2-1,
1, viewFinderSize.height+2)];
leftLine.backgroundColor = [UIColor grayColor];
[self.view addSubview:leftLine];
UIView *rightLine =
[[UIView alloc] initWithFrame:CGRectMake((readerFrame.size.width + viewFinderSize.width)/2,
(readerFrame.size.height - viewFinderSize.height)/2-1,
1, viewFinderSize.height+2)];
rightLine.backgroundColor = [UIColor grayColor];
[self.view addSubview:rightLine];
self.scanLineImageView =
[[UIImageView alloc] initWithFrame:CGRectMake((readerFrame.size.width - 230)/2,
(readerFrame.size.height - viewFinderSize.height)/2,
230, 10)];
self.scanLineImageView.image = [UIImage imageNamed:@"qr_scan_line"];
[self.view addSubview:self.scanLineImageView];
/* 画一个取景框结束 */
/* 配置取景框之外颜色开始 */
// scanCrop
UIView *viewTopScan =
[[UIView alloc] initWithFrame:CGRectMake(0, 0, mainBounds.size.width, scanCrop.origin.y)];
UIView *viewBottomScan =
[[UIView alloc] initWithFrame:CGRectMake(0, scanCrop.origin.y+scanCrop.size.height,
mainBounds.size.width,
mainBounds.size.height - scanCrop.size.height - scanCrop.origin.y)];
UIView *viewLeftScan =
[[UIView alloc] initWithFrame:CGRectMake(0, scanCrop.origin.y, scanCrop.origin.x, scanCrop.size.height)];
UIView *viewRightScan =
[[UIView alloc] initWithFrame:CGRectMake(scanCrop.origin.x + scanCrop.size.width,
scanCrop.origin.y,
mainBounds.size.width - scanCrop.origin.x - scanCrop.size.width,
scanCrop.size.height)];
viewTopScan.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.2];
viewBottomScan.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.2];
viewLeftScan.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.2];
viewRightScan.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.2];
[self.view addSubview:viewTopScan];
[self.view addSubview:viewBottomScan];
[self.view addSubview:viewLeftScan];
[self.view addSubview:viewRightScan];
/* 配置取景框之外颜色结束 */
// 返回键
UIButton *goBackButton =
({
UIButton *button =
[[UIButton alloc] initWithFrame:CGRectMake(20, 30, 36, 36)];
[button setImage:[UIImage imageNamed:@"qr_vc_left"] forState:UIControlStateNormal];
button.layer.cornerRadius = 18.0;
button.layer.backgroundColor = [[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5] CGColor];
[button addTarget:self action:@selector(goBack:) forControlEvents:UIControlEventTouchDown];
button;
});
[self.view addSubview:goBackButton];
// 控制散光灯
UIButton *torchSwitch =
({
UIButton *button =
[[UIButton alloc] initWithFrame:CGRectMake(mainBounds.size.width-44-20, 30, 36, 36)];
[button setImage:[UIImage imageNamed:@"qr_vc_right"] forState:UIControlStateNormal];
button.layer.cornerRadius = 18.0;
button.layer.backgroundColor = [[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5] CGColor];
[button addTarget:self action:@selector(torchSwitch:) forControlEvents:UIControlEventTouchDown];
button;
});
[self.view addSubview:torchSwitch];
self.infoLabel =
[[UILabel alloc] initWithFrame:CGRectMake((readerFrame.size.width - viewFinderSize.width)/2,
(readerFrame.size.height + viewFinderSize.height)/2 + 20,
viewFinderSize.width, 30)];
self.infoLabel.text = @"将二维码放入取景框中即可自动扫描";
self.infoLabel.font = [UIFont systemFontOfSize:13.0];
self.infoLabel.layer.cornerRadius = self.infoLabel.frame.size.height / 2;
self.infoLabel.layer.backgroundColor = [[UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5] CGColor];
self.infoLabel.textColor = [UIColor whiteColor];
self.infoLabel.textAlignment = NSTextAlignmentCenter;
[self.view addSubview:self.infoLabel];
self.view.backgroundColor = [UIColor blackColor];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self initViewAndSubViews];
}
- (void)viewWillAppear:(BOOL)animated
{
//
self.navigationController.navigationBar.hidden = YES;
UIApplication *application = [UIApplication sharedApplication];
[application setStatusBarHidden:YES];
[super viewWillAppear:animated];
//6.启动会话
[self.session startRunning];
}
- (void)viewDidAppear:(BOOL)animated
{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error = nil;
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if (error) {
NSLog(@"没有摄像头%@", error.localizedDescription);
input = nil;
[self.navigationController popViewControllerAnimated:YES];
return;
}
if (self.scanLineTimer == nil)
{
[self moveUpAndDownLine];
[self createTimer];
}
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
self.navigationController.navigationBar.hidden = NO;
UIApplication *application = [UIApplication sharedApplication];
[application setStatusBarHidden:NO];
[super viewWillDisappear:animated];
}
// 返回
- (void)goBack:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
// 控制散光灯
- (void)torchSwitch:(id)sender
{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error;
if (device.hasTorch)
{ // 判断设备是否有散光灯
BOOL b = [device lockForConfiguration:&error];
if (!b)
{
if (error)
{
NSLog(@"lock torch configuration error:%@", error.localizedDescription);
}
return;
}
device.torchMode =
(device.torchMode == AVCaptureTorchModeOff ? AVCaptureTorchModeOn : AVCaptureTorchModeOff);
[device unlockForConfiguration];
}
}
#define LINE_SCAN_TIME 3.0 // 扫描线从上到下扫描所历时间(s)
- (void)createTimer
{
self.scanLineTimer =
[NSTimer scheduledTimerWithTimeInterval:LINE_SCAN_TIME
target:self
selector:@selector(moveUpAndDownLine)
userInfo:nil
repeats:YES];
}
// 扫描条上下滚动
- (void)moveUpAndDownLine
{
CGRect readerFrame = self.view.frame;
CGSize viewFinderSize = CGSizeMake(self.view.frame.size.width - 80, self.view.frame.size.width - 80);
CGRect scanLineframe = self.scanLineImageView.frame;
scanLineframe.origin.y =
(readerFrame.size.height - viewFinderSize.height)/2;
self.scanLineImageView.frame = scanLineframe;
self.scanLineImageView.hidden = NO;
__weak __typeof(self) weakSelf = self;
[UIView animateWithDuration:LINE_SCAN_TIME - 0.05
animations:^{
CGRect scanLineframe = weakSelf.scanLineImageView.frame;
scanLineframe.origin.y =
(readerFrame.size.height + viewFinderSize.height)/2 -
weakSelf.scanLineImageView.frame.size.height;
weakSelf.scanLineImageView.frame = scanLineframe;
}
completion:^(BOOL finished) {
weakSelf.scanLineImageView.hidden = YES;
}];
}
#pragma mark AVCaptureMetadataOutputObjectsDelegate
//此方法是在识别到QRCode并且完成转换,如果QRCode的内容越大,转换需要的时间就越长。
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
// 会频繁的扫描,调用代理方法
// 1如果扫描完成,停止会话
[self.session stopRunning];
//2删除预览图层
[self.previewLayer removeFromSuperlayer];
//设置界面显示扫描结果
if (metadataObjects.count > 0)
{
AVMetadataMachineReadableCodeObject *obj = metadataObjects[0];
if ([self.delegate respondsToSelector:@selector(didFinishedReadingQR:)])
{
[self.delegate didFinishedReadingQR:obj.stringValue];
}
}
[self.navigationController popViewControllerAnimated:YES];
}
@end
#import "ViewController.h"
#import "QRReaderViewController.h"
@interface ViewController () <QRReaderViewControllerDelegate>
@property (nonatomic, strong) UIButton *showQRReaderButton;
@property (nonatomic, strong) UILabel *qrReaderResultLabel;
@end
@implementation ViewController
- (void)initViewAndSubViews {
self.view.backgroundColor = [UIColor whiteColor];
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
self.showQRReaderButton =
[[UIButton alloc] initWithFrame:CGRectMake(0, 100, screenWidth, 44)];
[self.showQRReaderButton setTitle:@"Show QR Reader" forState:UIControlStateNormal];
[self.showQRReaderButton setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
self.showQRReaderButton.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.showQRReaderButton];
[self.showQRReaderButton addTarget:self action:@selector(showQRReader:)
forControlEvents:UIControlEventTouchDown];
self.qrReaderResultLabel =
[[UILabel alloc] initWithFrame:CGRectMake(0, 200, screenWidth, 44)];
self.qrReaderResultLabel.textAlignment = NSTextAlignmentCenter;
self.qrReaderResultLabel.font = [UIFont systemFontOfSize:14.0];
self.qrReaderResultLabel.textColor = [UIColor grayColor];
self.qrReaderResultLabel.numberOfLines = 0;
[self.view addSubview:self.qrReaderResultLabel];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self initViewAndSubViews];
}
// 读二维码
- (void)showQRReader:(id)sender {
// 扫描二维码
// 1. init ViewController
QRReaderViewController *VC = [[QRReaderViewController alloc] init];
// 2. configure ViewController
VC.delegate = self;
// 3. show ViewController
[self.navigationController pushViewController:VC animated:YES];
}
#pragma mark - QRReaderViewControllerDelegate
- (void)didFinishedReadingQR:(NSString *)string {
NSLog(@"result string: %@", string);
self.qrReaderResultLabel.text = string;
}
@end
转自:http://zhangbuhuai.com/2015/02/11/iOS原生二维码扫描/