iOS 二維碼掃描(你想要的都在這里了)

以前就寫過二維碼掃描的文章,今天難得抽出來時間重新整理了一下,把所有用都的關于二維碼的都寫在這了,二維碼問題,看了這一篇文章就什么都解決了

原生二維碼掃描

個人是比較支持用原生二維碼掃描的,這里也就僅僅以原生二維碼掃面為范例。另也有二維碼掃描庫ZBarSDK(點這里) ZXingObjC(點這里)

1.原生掃描用到的幾個類

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

2.在viewDidLoad里創建它們

// Device
_device = [AVCaptureDevicedefaultDeviceWithMediaType:AVMediaTypeVideo];

// Input
_input = [AVCaptureDeviceInputdeviceInputWithDevice:self.deviceerror:nil];

// Output
_output = [[AVCaptureMetadataOutputalloc]init];
[_outputsetMetadataObjectsDelegate:selfqueue:dispatch_get_main_queue()];

// Session
_session = [[AVCaptureSessionalloc]init];
[_sessionsetSessionPreset:AVCaptureSessionPresetHigh];

連接輸入和輸出

if ([_sessioncanAddInput:self.input])
{
    [_sessionaddInput:self.input];
}

if ([_sessioncanAddOutput:self.output])
{
    [_sessionaddOutput:self.output];
}

設置條碼類型

_output.metadataObjectTypes =@[AVMetadataObjectTypeQRCode];

添加掃描畫面

_preview =[AVCaptureVideoPreviewLayerlayerWithSession:_session];
_preview.videoGravity =AVLayerVideoGravityResizeAspectFill;
_preview.frame =self.view.layer.bounds;
[self.view.layerinsertSublayer:_previewatIndex:0];

開始掃描

[_sessionstartRunning];

最后實現協議AVCaptureMetadataOutputObjectsDelegate

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
   NSString *stringValue;
   if ([metadataObjectscount] >0){
   //停止掃描
   [_sessionstopRunning];        
   AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjectsobjectAtIndex:0];
   stringValue = metadataObject.stringValue;        
  }
}

到了這一步就可以成功掃描二維碼了

還有一個小問題需要說明一下,之前寫過一篇限制二維碼掃描區域的,在這里稍微提一下。如下圖微信掃描,把掃描范圍限制在中間的方框內

微信二維碼掃描

要想限制二維碼掃描區域,需要設置一個參數rectOfInterest 這個參數有點特別,這個參數的react 與平常設置的坐標系是完全相反的,即X與Y互換、W與H互換。

PS: 有好多人問我上圖中方形的透明遮罩是怎么弄得,其實我在提供的demo里已經加上了透明遮罩,你可以直接下載demo去查看。
附上demo地址:https://github.com/liutongchao/LCQRCodeUtil

設置的方法(掃描demo中已經加入了設置掃描區域)

  [_output setRectOfInterest:CGRectMake()];

如果你感到疑惑那么可以看一下我寫的這篇文章,里面有詳細研究。

iOS 原生二維碼掃描(可限制掃描區域)

生成二維碼圖片

生成二維碼和掃描二維碼圖片就比較簡單了,自己也寫了個工具類來處理,這里直接貼出來代碼,后面也會有代碼可以下載

在這里借鑒的有別人的文章,想看原文點這里

首先LCQrcodeUtil.h中
/**
 *  生成二維碼圖片
 *
 *  @param QRString  二維碼內容
 *  @param sizeWidth 圖片size(正方形)
 *  @param color     填充色
 *
 *  @return  二維碼圖片
 */
+(UIImage *)createQRimageString:(NSString *)QRString sizeWidth:(CGFloat)sizeWidth fillColor:(UIColor *)color;
LCQrcodeUtil.m中
#pragma mark 生成二維碼
/**
 *  生成二維碼圖片
 *
 *  @param QRString  二維碼內容
 *  @param sizeWidth 圖片size(正方形)
 *  @param color     填充色
 *
 *  @return  二維碼圖片
 */
+(UIImage *)createQRimageString:(NSString *)QRString sizeWidth:(CGFloat)sizeWidth fillColor:(UIColor *)color{
CIImage *ciimage = [self createQRForString:QRString];
UIImage *qrcode = [self createNonInterpolatedUIImageFormCIImage:ciimage withSize:sizeWidth];
if (color) {
    CGFloat R, G, B;
    
    CGColorRef colorRef = [color CGColor];
    long numComponents = CGColorGetNumberOfComponents(colorRef);
    
    if (numComponents == 4)
    {
        const CGFloat *components = CGColorGetComponents(colorRef);
        R = components[0];
        G = components[1];
        B = components[2];
    }
    
    UIImage *customQrcode = [self imageBlackToTransparent:qrcode withRed:R andGreen:G andBlue:B];
    return customQrcode;
}

return qrcode;

}
pragma mark
   #pragma mark - QRCodeGenerator
+ (CIImage *)createQRForString:(NSString *)qrString {
// Need to convert the string to a UTF-8 encoded NSData object
NSData *stringData = [qrString dataUsingEncoding:NSUTF8StringEncoding];
// Create the filter
CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
// Set the message content and error-correction level
[qrFilter setValue:stringData forKey:@"inputMessage"];
[qrFilter setValue:@"M" forKey:@"inputCorrectionLevel"];
// Send the image back
return qrFilter.outputImage;
}
pragma mark
#pragma mark - InterpolatedUIImage
+ (UIImage *)createNonInterpolatedUIImageFormCIImage:(CIImage *)image withSize:(CGFloat) size {
CGRect extent = CGRectIntegral(image.extent);
CGFloat scale = MIN(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent));
// create a bitmap image that we'll draw into a bitmap context at the desired size;
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);
// Create an image with the contents of our bitmap
CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
// Cleanup
CGContextRelease(bitmapRef);
CGImageRelease(bitmapImage);
return [UIImage imageWithCGImage:scaledImage];
}
pragma mark
#pragma mark - imageToTransparent
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);
// create context
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace,
                                             kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image.CGImage);
// traverse pixe
int pixelNum = imageWidth * imageHeight;
uint32_t* pCurPtr = rgbImageBuf;
for (int i = 0; i < pixelNum; i++, pCurPtr++){
    if ((*pCurPtr & 0xFFFFFF00) < 0x99999900){
        // change color
        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;
    }
}
// context to image
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];
// release
CGImageRelease(imageRef);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
return resultUIImage;
}

解析二維碼圖片

還是以源碼的形式貼出來
這里也有借鑒別人的代碼,點這里 查看原文

首先LCQrcodeUtil.h中
/**
 *  讀取圖片中二維碼信息
 *
 *  @param image 圖片
 *
 *  @return 二維碼內容
 */
+(NSString *)readQRCodeFromImage:(UIImage *)image;
LCQrcodeUtil.m中
#pragma mark 讀取圖片二維碼
/**
 *  讀取圖片中二維碼信息
 *
 *  @param image 圖片
 *
 *  @return 二維碼內容
 */
+(NSString *)readQRCodeFromImage:(UIImage *)image{
NSData *data = UIImagePNGRepresentation(image);
CIImage *ciimage = [CIImage imageWithData:data];
if (ciimage) {
    CIDetector *qrDetector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:[CIContext contextWithOptions:@{kCIContextUseSoftwareRenderer:@(YES)}] options:@{CIDetectorAccuracy : CIDetectorAccuracyHigh}];
    NSArray *resultArr = [qrDetector featuresInImage:ciimage];
    if (resultArr.count >0) {
        CIFeature *feature = resultArr[0];
        CIQRCodeFeature *qrFeature = (CIQRCodeFeature *)feature;
        NSString *result = qrFeature.messageString;
        
        return result;
    }else{
        return nil;
    }
}else{
    return nil;
}

}

都是直接封裝成類方法調用,這個類我也已經上傳到git,不想CV的可以到git中下載,地址:
https://github.com/liutongchao/LCQRCodeUtil

有問題歡迎指正以及互相探討 -- LC.West

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容