iOS QRCode(二维码)操作

扫描主要使用的是AVFoundation 使用起来也非常的简单 正常的初始化流程如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@interface Example1Controller()
<
AVCaptureMetadataOutputObjectsDelegate,
UIAlertViewDelegate
>

@property (nonatomic, strong) UIView *scanRectView;

@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;

@end

@implementation Example1Controller

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
    self.input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil];
    
    self.output = [[AVCaptureMetadataOutput alloc]init];
    [self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    
    self.session = [[AVCaptureSession alloc]init];
    [self.session setSessionPreset:([UIScreen mainScreen].bounds.size.height<500)?AVCaptureSessionPreset640x480:AVCaptureSessionPresetHigh];
    [self.session addInput:self.input];
    [self.session addOutput:self.output];
    self.output.metadataObjectTypes=@[AVMetadataObjectTypeQRCode];
    self.output.rectOfInterest = scanRect;
    
    self.preview = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
    self.preview.videoGravity = AVLayerVideoGravityResizeAspectFill;
    self.preview.frame = [UIScreen mainScreen].bounds;
    [self.view.layer insertSublayer:self.preview atIndex:0];
    
    //开始捕获
    [self.session startRunning];
    
}

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    if ( (metadataObjects.count==0) )
    {
        return;
    }
    
    if (metadataObjects.count>0) {
        
        [self.session stopRunning];
        
        AVMetadataMachineReadableCodeObject *metadataObject = metadataObjects.firstObject;
        //输出扫描字符串
        
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:metadataObject.stringValue message:@"" delegate:self cancelButtonTitle:@"ok" otherButtonTitles: nil];
        
        [alert show];
    }
}

- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex
{
    [self.session startRunning];
}

AVFoundation的部分我就不多介绍了 只要按照上面的代码初始化 即可实现二维码扫描的功能 是不是很简单?

不过这里还不能完全满足我们的要求 因为如果运行以后你会发现 现在是全屏扫描 而不是像我们印象中的那种在屏幕中间有个框 只有在框中的二维码才会被扫描到 不过其实改起来也很简单 AVCaptureMetadataOutput有个属性rectOfInterest就是做这个事情的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@interface AVCaptureMetadataOutput : AVCaptureOutput 

/*!
 @property rectOfInterest
 @abstract
	Specifies a rectangle of interest for limiting the search area for visual metadata.
 
 @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.
 */
@property(nonatomic) CGRect rectOfInterest NS_AVAILABLE_IOS(7_0);

@end

可以看到 rectOfInterest的值的范围都是0-1 是按比例取值而不是实际尺寸 不过其实也很简单 只要换算一下就好了 接下来我们添加取景框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CGSize windowSize = [UIScreen mainScreen].bounds.size;

CGSize scanSize = CGSizeMake(windowSize.width*3/4, windowSize.width*3/4);
CGRect scanRect = CGRectMake((windowSize.width-scanSize.width)/2, (windowSize.height-scanSize.height)/2, scanSize.width, scanSize.height);

//计算rectOfInterest 注意x,y交换位置
scanRect = CGRectMake(scanRect.origin.y/windowSize.height, scanRect.origin.x/windowSize.width, scanRect.size.height/windowSize.height,scanRect.size.width/windowSize.width);
self.output.rectOfInterest = scanRect;

self.scanRectView = [UIView new];
[self.view addSubview:self.scanRectView];
self.scanRectView.frame = CGRectMake(0, 0, scanSize.width, scanSize.height);
self.scanRectView.center = CGPointMake(CGRectGetMidX([UIScreen mainScreen].bounds), CGRectGetMidY([UIScreen mainScreen].bounds));
self.scanRectView.layer.borderColor = [UIColor redColor].CGColor;
self.scanRectView.layer.borderWidth = 1;

这里唯一要注意的一点是 rectOfInterest 都是按照横屏来计算的 所以当竖屏的情况下 x轴和y轴要交换一下

读取


读取主要用到CoreImage 不过要强调的是读取二维码的功能只有在iOS8之后才支持
读取的代码实现就更简单了

1
2
3
4
5
6
7
8
9
UIImage * srcImage = qrcodeImage;

CIContext *context = [CIContext contextWithOptions:nil];
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:context options:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}];
CIImage *image = [CIImage imageWithCGImage:srcImage.CGImage];
NSArray *features = [detector featuresInImage:image];
CIQRCodeFeature *feature = [features firstObject];

NSString *result = feature.messageString;





生成


生成也是用到CoreImage 其步骤稍微多一点 代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
NSString *text = self.tfCode.text;

NSData *stringData = [text dataUsingEncoding: NSUTF8StringEncoding];

//生成
CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
[qrFilter setValue:stringData forKey:@"inputMessage"];
[qrFilter setValue:@"M" forKey:@"inputCorrectionLevel"];

UIColor *onColor = [UIColor redColor];
UIColor *offColor = [UIColor blueColor];

//上色
CIFilter *colorFilter = [CIFilter filterWithName:@"CIFalseColor"
                                   keysAndValues:
                         @"inputImage",qrFilter.outputImage,
                         @"inputColor0",[CIColor colorWithCGColor:onColor.CGColor],
                         @"inputColor1",[CIColor colorWithCGColor:offColor.CGColor],
                         nil];

CIImage *qrImage = colorFilter.outputImage;

//绘制
CGSize size = CGSizeMake(300, 300);
CGImageRef cgImage = [[CIContext contextWithOptions:nil] createCGImage:qrImage fromRect:qrImage.extent];
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGContextGetClipBoundingBox(context), cgImage);
UIImage *codeImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

CGImageRelease(cgImage);

  • 首先通过[CIFilter filterWithName:@"CIQRCodeGenerator"]生成QRCode
  • 然后通过[CIFilter filterWithName:@"CIFalseColor"]上色(当然这一步不是必须的 如果仅仅需要白底黑块的QRCode 可以跳过这一步)
  • 最后无损放大并绘制QRCode (上面两步生成的QRCode很小 大概是31*31 如果不放大 就会很模糊)

这里要注意的是 在最后一步一定要使用CGContextScaleCTM(context, 1.0, -1.0)来翻转一下图片 不然生成的QRCode就是上下颠倒

原文参考:再见ZXing 使用系统原生代码处理QRCode


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值