iOS之使用二维码扫描和生成二维码

参考: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:@"确定" otherButtonTitlesnil];

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

}

经过这样的处理,基本上二维码就成型了,如果还想加上阴影,就在ImageViewLayer上使用下面代码添加阴影:


ImageView.layer.shadowOffset = CGSizeMake(0, 0.5);  // 设置阴影的偏移量

ImageView.layer.shadowRadius = 1;  // 设置阴影的半径

ImageView.layer.shadowColor = [UIColor blackColor].CGColor; // 设置阴影的颜色为黑色

ImageView.layer.shadowOpacity = 0.3; // 设置阴影的不透明度




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值