二維碼的生成有好多第三方庫,如Z-Xing。但是為了控制安裝包的大小,或者并不需要其他的一些額外的功能,用系統(tǒng)的方法即可滿足.
一、二維碼的生成
+ (UIImage *)qrImageForString:(NSString *)string imageSize:(CGFloat)Imagesize logoImageSize:(CGFloat)waterImagesize{
CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
[filter setDefaults];
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
[filter setValue:data forKey:@"inputMessage"];//通過kvo方式給一個字符串,生成二維碼
[filter setValue:@"H" forKey:@"inputCorrectionLevel"];//設置二維碼的糾錯水平,越高糾錯水平越高,可以污損的范圍越大
CIImage *outPutImage = [filter outputImage];//拿到二維碼圖片
return [[self alloc] createNonInterpolatedUIImageFormCIImage:outPutImage withSize:Imagesize waterImageSize:waterImagesize];
}
- (UIImage *)createNonInterpolatedUIImageFormCIImage:(CIImage *)image withSize:(CGFloat) size waterImageSize:(CGFloat)waterImagesize{
CGRect extent = CGRectIntegral(image.extent);
CGFloat scale = MIN(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent));
// 1.創(chuàng)建bitmap;
size_t width = CGRectGetWidth(extent) * scale;
size_t height = CGRectGetHeight(extent) * scale;
//創(chuàng)建一個DeviceGray顏色空間
CGColorSpaceRef cs = CGColorSpaceCreateDeviceGray();
//CGBitmapContextCreate(void * _Nullable data, size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow, CGColorSpaceRef _Nullable space, uint32_t bitmapInfo)
//width:圖片寬度像素
//height:圖片高度像素
//bitsPerComponent:每個顏色的比特值,例如在rgba-32模式下為8
//bitmapInfo:指定的位圖應該包含一個alpha通道。
CGContextRef bitmapRef = CGBitmapContextCreate(nil, width, height, 8, 0, cs, (CGBitmapInfo)kCGImageAlphaNone);
CIContext *context = [CIContext contextWithOptions:nil];
//創(chuàng)建CoreGraphics image
CGImageRef bitmapImage = [context createCGImage:image fromRect:extent];
CGContextSetInterpolationQuality(bitmapRef, kCGInterpolationNone);
CGContextScaleCTM(bitmapRef, scale, scale);
CGContextDrawImage(bitmapRef, extent, bitmapImage);
// 2.保存bitmap到圖片
CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
CGContextRelease(bitmapRef); CGImageRelease(bitmapImage);
//原圖
UIImage *outputImage = [UIImage imageWithCGImage:scaledImage];
//給二維碼加 logo 圖
UIGraphicsBeginImageContextWithOptions(outputImage.size, NO, [[UIScreen mainScreen] scale]);
[outputImage drawInRect:CGRectMake(0,0 , size, size)];
//logo圖
UIImage *waterimage = [UIImage imageNamed:@"icon_imgApp"];
//把logo圖畫到生成的二維碼圖片上,注意尺寸不要太大(最大不超過二維碼圖片的%30),太大會造成掃不出來
[waterimage drawInRect:CGRectMake((size-waterImagesize)/2.0, (size-waterImagesize)/2.0, waterImagesize, waterImagesize)];
UIImage *newPic = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newPic;
}
這個是官方文檔中對inputMessage和inputCorrectionLevel的解釋
[CIQRCodeGenerator ](https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CoreImageFilterReference/#//apple_ref/doc/filter/ci/CIQRCodeGenerator)
Generates a Quick Response code (two-dimensional barcode) from input data.
Parameters
*inputMessage*
The data to be encoded as a QR code. AnNSData
object whose display name is Message.
*inputCorrectionLevel*
A single letter specifying the error correction format. AnNSString
object whose display name is CorrectionLevel.
Default value:M
Discussion
Generates an output image representing the input data according to the ISO/IEC 18004:2006 standard. The width and height of each module (square dot) of the code in the output image is one point. To create a QR code from a string or URL, convert it to an[NSData](https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/index.html#//apple_ref/occ/cl/NSData)object using the[NSISOLatin1StringEncoding](https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/index.html#//apple_ref/c/econst/NSISOLatin1StringEncoding)string encoding.
The*inputCorrectionLevel*
parameter controls the amount of additional data encoded in the output image to provide error correction. Higher levels of error correction result in larger output images but allow larger areas of the code to be damaged or obscured without. There are four possible correction modes (with corresponding error resilience levels):
L : 7%
M : 15%
Q : 25%
H : 30%
Member Of
CICategoryBuiltIn
,CICategoryStillImage
,CICategoryGenerator
Sample Output
**Figure 91**The result of using the CIQRCodeGenerator filter 
修改二維碼的顏色,這一段是在網上找的,把生成的二維碼圖片傳入,再傳入想要的顏色即可
圖片要傳沒有加過 logo 的
- (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 < pixelNum; i++, pCurPtr++){
if ((*pCurPtr & 0xFFFFFF00) < 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, nil);
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;
}
二、掃碼
掃碼主要用到的是AVFoundation用到的東西和相機基本相同,相機請參考我的另外一篇文章
聲明以下對象,遵守AVCaptureMetadataOutputObjectsDelegate
@interface ScanQRViewController ()<AVCaptureMetadataOutputObjectsDelegate>
//捕獲設備,通常是前置攝像頭,后置攝像頭,麥克風(音頻輸入)
@property(nonatomic)AVCaptureDevice *device;
//AVCaptureDeviceInput 代表輸入設備,他使用AVCaptureDevice 來初始化
@property(nonatomic)AVCaptureDeviceInput *input;
//設置輸出類型為Metadata,因為這種輸出類型中可以設置掃描的類型,譬如二維碼
//當啟動攝像頭開始捕獲輸入時,如果輸入中包含二維碼,就會產生輸出
@property(nonatomic)AVCaptureMetadataOutput *output;
//session:由他把輸入輸出結合在一起,并開始啟動捕獲設備(攝像頭)
@property(nonatomic)AVCaptureSession *session;
//圖像預覽層,實時顯示捕獲的圖像
@property(nonatomic)AVCaptureVideoPreviewLayer *previewLayer;
初始化各對象,輸入輸出設備結合
- (void)creatCaptureDevice{
//使用AVMediaTypeVideo 指明self.device代表視頻,默認使用后置攝像頭進行初始化
self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
//使用設備初始化輸入
self.input = [[AVCaptureDeviceInput alloc]initWithDevice:self.device error:nil];
//生成輸出對象
self.output = [[AVCaptureMetadataOutput alloc]init];
//設置代理,一旦掃描到指定類型的數據,就會通過代理輸出
//在掃描的過程中,會分析掃描的內容,分析成功后就會調用代理方法在隊列中輸出
[self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
//生成會話,用來結合輸入輸出
self.session = [[AVCaptureSession alloc]init];
if ([self.session canAddInput:self.input]) {
[self.session addInput:self.input];
}
if ([self.session canAddOutput:self.output]) {
[self.session addOutput:self.output];
}
//指定當掃描到二維碼的時候,產生輸出
//AVMetadataObjectTypeQRCode 指定二維碼
//指定識別類型一定要放到添加到session之后
[self.output setMetadataObjectTypes:@[AVMetadataObjectTypeQRCode]];
//設置掃描信息的識別區(qū)域,左上角為(0,0),右下角為(1,1),不設的話全屏都可以識別。設置過之后可以縮小信息掃描面積加快識別速度。
//這個屬性并不好設置,整了半天也沒太搞明白,到底x,y,width,height,怎么是對應的,這是我一點一點試的掃描區(qū)域,看不到只能調一下,掃一掃試試
[self.output setRectOfInterest:CGRectMake(0.1 ,0.3 , 0.4, 0.4)];
//使用self.session,初始化預覽層,self.session負責驅動input進行信息的采集,layer負責把圖像渲染顯示
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.session];
self.previewLayer.frame = CGRectMake(0, 0, kScreenWidth , kScreenHeight);
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.view.layer addSublayer:self.previewLayer];
//開始啟動
[self.session startRunning];
}
實現代理方法
#pragma mark 輸出的代理
//metadataObjects :把識別到的內容放到該數組中
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
//停止掃描
[self.session stopRunning];
[self.timer invalidate];
self.timer = nil;
[self.lineView removeFromSuperview];
if ([metadataObjects count] >= 1) {
//數組中包含的都是AVMetadataMachineReadableCodeObject 類型的對象,該對象中包含解碼后的數據
AVMetadataMachineReadableCodeObject *qrObject = [metadataObjects lastObject];
拿到掃描內容在這里進行個性化處理
NSLog(@"識別成功%@",qrObject.stringValue);
}
}
三、遇到的問題和解決辦法
(1)二維碼上加logo圖的時候,圖片很模糊,這是由于UIGraphicsBeginImageContextWithOptions里的 scale 造成的,由于 iPhone 的屏幕都是retina屏幕,都是2倍,3倍像素,這里的 scale 要根據屏幕來設置 即[[UIScreen mainScreen] scale]這樣圖片就會很清晰
(2)setRectOfInterest:設置掃描信息的識別區(qū)域,左上角為(0,0),右下角為(1,1),不設的話全屏都可以識別。設置過之后可以縮小信息掃描面積加快識別速度,原來掃描的是整個屏幕的大小,這時候只掃描一塊區(qū)域,以此加快識別速度。但是這個屬性并不好設置,整了半天也沒太搞明白,到底x,y,width,height,怎么是對應的,而且是比例不是直接的數字,我是一點一點試的掃描區(qū)域,看不到情況,只能調一下,掃一掃試試
最后也沒整明白,哪位大神知道,求解答
附上官方解釋:
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.