参考:http://blog.csdn.net/leonliu070602/article/details/51396261
二维码中的坑:
二维码扫描功能,因为app支持IOS7以上版本,所以选择了iOS的原生二维码扫描AVFoundation。上线后部分玩家反映点击就崩溃,找到同样的iphone,同样的ios版本没有任何崩溃的现象,疯了!
道设置output.metadataObjectTypes=@[AVMetadataObjectTypeQRCode]的时候崩溃了,后来发现output.availableMetadataObjectTypes为空,却不知为何为空。原来是把权限关了,然后打开权限,再扫描就ok了,没有再闪退。
后来在扫描之前加了判断相机的访问权限,贴上代码:
NSString *mediaType = AVMediaTypeVideo;
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:mediaType];
if(authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied){
UIAlertView *alert =[[UIAlertView alloc]initWithTitle:@“XXXX”message:@"请在iPhone的“设置”-“隐私”-“相机”功能中,找到“XXXX”打开相机访问权限" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles: nil];
[alert show];
return;
}
==========使用二维码扫苗========
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
@interface HMPreView :UIView
@property (nonatomic,strong)AVCaptureSession *session;
@end
#import "HMPreView.h"
@interface HMPreView ()
@property (nonatomic,strong)UIImageView *imageView;
@property (nonatomic,strong)UIImageView *lineImageView;
@property (nonatomic,strong)NSTimer *timer;
@end
@implementation HMPreView
/**
* layer的类型
*
* @return AVCaptureVideoPreviewLayer 特殊的layer 可以展示输入设备采集到得信息
*/
+ (Class)layerClass
{
return [AVCaptureVideoPreviewLayerclass];
}
- (void)setSession:(AVCaptureSession *)session
{
_session = session;
AVCaptureVideoPreviewLayer *layer = (AVCaptureVideoPreviewLayer *) self.layer;
layer.session = session;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [superinitWithFrame:frame]) {
[self initUiConfig];
}
returnself;
}
- (void)initUiConfig
{
//设置背景图片
_imageView = [[UIImageViewalloc]initWithImage:[UIImageimageNamed:@"pick_bg.png"]];
//设置位置到界面的中间
_imageView.frame =CGRectMake(self.bounds.size.width * 0.5 - 140, self.bounds.size.height * 0.5 - 140, 280,280);
//添加到视图上
[selfaddSubview:_imageView];
//初始化二维码的扫描线的位置
_lineImageView = [[UIImageViewalloc]initWithFrame:CGRectMake(30,10,220,2)];
_lineImageView.image = [UIImageimageNamed:@"line.png"];
[_imageViewaddSubview:_lineImageView];
//开启定时器
_timer = [NSTimerscheduledTimerWithTimeInterval:3target:selfselector:@selector(animation)userInfo:nilrepeats:YES];
}
- (void)animation
{
[UIViewanimateWithDuration:2.8delay:0options:UIViewAnimationOptionCurveLinearanimations:^{
_lineImageView.frame =CGRectMake(30,260,220,2);
} completion:^(BOOL finished) {
_lineImageView.frame =CGRectMake(30,10,220,2);
}];
}
#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#import "HMPreView.h"
/** 补充的iOS9新特性*/
#import <SafariServices/SafariServices.h>
/**
AV
A : Audio 声音
V : Video 视频
*/
@interfaceViewController ()<AVCaptureMetadataOutputObjectsDelegate>
/** 输入设备*/
@property (nonatomic,strong)AVCaptureDeviceInput *input;
/** 数据输出*/
@property (nonatomic,strong)AVCaptureMetadataOutput *output;
/** 会话类*/
@property (nonatomic,strong)AVCaptureSession *session;
/**特殊的layer,展示用户扫描到的内容*/
//@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
@property (nonatomic,strong)HMPreView *preview;
@end
@implementation ViewController
- (void)viewDidLoad {
[superviewDidLoad];
}
#pragma mark 点击屏幕扫描二维码
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//1. 输入设备
AVCaptureDevice *device = [AVCaptureDevicedefaultDeviceWithMediaType:AVMediaTypeVideo];
self.input = [[AVCaptureDeviceInputalloc]initWithDevice:deviceerror:nil];
//2. 数据输出
//2.1 创建数据输出对象
self.output = [AVCaptureMetadataOutputnew];
//2.2 设置代理
[self.outputsetMetadataObjectsDelegate:selfqueue:dispatch_get_main_queue()];
//3. 会话类 -->协调输入和输出
//3.1 创建会话类
self.session = [AVCaptureSessionnew];
//3.2 协调输入和输出
if ([self.sessioncanAddInput:self.input]) {
[self.sessionaddInput:self.input];
}
if ([self.sessioncanAddOutput:self.output]) {
[self.sessionaddOutput:self.output];
}
//2.3 设置扫描类型二维码 : QRCode
[self.outputsetMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
//4. 特殊的layer,展示用户扫描到的内容 -->需要一个会话类的对象
// self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
// self.previewLayer.frame = self.view.bounds;
// [self.view.layer addSublayer:self.previewLayer];
self.preview = [[HMPreViewalloc]initWithFrame:self.view.bounds];
self.preview.session =self.session;
[self.viewaddSubview:self.preview];
//5. 开始扫描
[self.sessionstartRunning];
}
#pragma mark 代理方法 -->获取数据
//metadataObjects: 扫描到的数据
//fromConnection: 连接
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
//1. 停止扫描
[self.sessionstopRunning];
//2. 删除特殊的layer
//[self.previewLayer removeFromSuperlayer];
[self.previewremoveFromSuperview];
//3. 获取数据
//AVMetadataMachineReadableCodeObject:这个类如果不知道,可以打印一下
for (AVMetadataMachineReadableCodeObject *objin metadataObjects) {
//4. 将来就在这里进行数据处理
// 判断网页并弹出
if ([obj.stringValuecontainsString:@"http"]) {
//4.1 创建SafariVC
SFSafariViewController *sfVC = [[SFSafariViewControlleralloc]initWithURL:[NSURLURLWithString:obj.stringValue]];
//4.2 弹出SafariVC
[selfpresentViewController:sfVCanimated:YEScompletion:nil];
}
}
}
@end
@property(nonatomic,strong)UIImageView *customImageView;
首先是二维码的生成,使用CIFilter很简单,直接传入生成二维码的字符串即可:
- (CIImage *)createQRForString:(NSString *)qrString {
NSData *stringData = [qrString dataUsingEncoding:NSUTF8StringEncoding];
// 创建filter
CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
// 设置内容和纠错级别
[qrFilter setValue:stringData forKey:@"inputMessage"];
[qrFilter setValue:@"M" forKey:@"inputCorrectionLevel"];
// 返回CIImage
return qrFilter.outputImage;
}
self.customImageView.image=[UIImage imageWithCIImage:ciImage];//普通二维码
下面是生成高清二维码并且设置大小;因为生成的二维码是一个CIImage,我们直接转换成UIImage的话大小不好控制,所以使用下面方法返回需要大小的UIImage:
- (UIImage *)createNonInterpolatedUIImageFormCIImage:(CIImage *)image withSize:(CGFloat) size {
CGRect extent = CGRectIntegral(image.extent);
CGFloat scale = MIN(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent));
// 创建bitmap;
size_t width = CGRectGetWidth(extent) * scale;
size_t height = CGRectGetHeight(extent) * scale;
CGColorSpaceRef cs = CGColorSpaceCreateDeviceGray();
CGContextRef bitmapRef = CGBitmapContextCreate(nil, width, height, 8, 0, cs, (CGBitmapInfo)kCGImageAlphaNone);
CIContext *context = [CIContext contextWithOptions:nil];
CGImageRef bitmapImage = [context createCGImage:image fromRect:extent];
CGContextSetInterpolationQuality(bitmapRef, kCGInterpolationNone);
CGContextScaleCTM(bitmapRef, scale, scale);
CGContextDrawImage(bitmapRef, extent, bitmapImage);
// 保存bitmap到图片
CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
CGContextRelease(bitmapRef);
CGImageRelease(bitmapImage);
return [UIImage imageWithCGImage:scaledImage];
}
因为生成的二维码是黑白的,所以还要对二维码进行颜色填充,并转换为透明背景,使用遍历图片像素来更改图片颜色,因为使用的是CGContext,速度非常快:
void ProviderReleaseData (void *info, const void *data, size_t size){
free((void*)data);
}
- (UIImage*)imageBlackToTransparent:(UIImage*)image withRed:(CGFloat)red andGreen:(CGFloat)green andBlue:(CGFloat)blue{
const int imageWidth = image.size.width;
const int imageHeight = image.size.height;
size_t bytesPerRow = imageWidth * 4;
uint32_t* rgbImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace,
kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image.CGImage);
// 遍历像素
int pixelNum = imageWidth * imageHeight;
uint32_t* pCurPtr = rgbImageBuf;
for (int i = 0; i <</span> pixelNum; i++, pCurPtr++){
if ((*pCurPtr & 0xFFFFFF00) <</span> 0x99999900) // 将白色变成透明
{
// 改成下面的代码,会将图片转成想要的颜色
uint8_t* ptr = (uint8_t*)pCurPtr;
ptr[3] = red; //0~255
ptr[2] = green;
ptr[1] = blue;
}
else
{
uint8_t* ptr = (uint8_t*)pCurPtr;
ptr[0] = 0;
}
}
// 输出图片
CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow * imageHeight, ProviderReleaseData);
CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight, 8, 32, bytesPerRow, colorSpace,
kCGImageAlphaLast | kCGBitmapByteOrder32Little, dataProvider,
NULL, true, kCGRenderingIntentDefault);
CGDataProviderRelease(dataProvider);
UIImage* resultUIImage = [UIImage imageWithCGImage:imageRef];
// 清理空间
CGImageRelease(imageRef);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
return resultUIImage;
}
经过这样的处理,基本上二维码就成型了,如果还想加上阴影,就在ImageView的Layer上使用下面代码添加阴影:
ImageView.layer.shadowOffset = CGSizeMake(0, 0.5); // 设置阴影的偏移量
ImageView.layer.shadowRadius = 1; // 设置阴影的半径
ImageView.layer.shadowColor = [UIColor blackColor].CGColor; // 设置阴影的颜色为黑色
ImageView.layer.shadowOpacity = 0.3; // 设置阴影的不透明度