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

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

原生二維碼掃描

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

1.原生掃描用到的幾個(gè)類

@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里創(chuàng)建它們

// 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];
}

設(shè)置條碼類型

_output.metadataObjectTypes =@[AVMetadataObjectTypeQRCode];

添加掃描畫面

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

開始掃描

[_sessionstartRunning];

最后實(shí)現(xiàn)協(xié)議AVCaptureMetadataOutputObjectsDelegate

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

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

還有一個(gè)小問題需要說明一下,之前寫過一篇限制二維碼掃描區(qū)域的谈撒,在這里稍微提一下腥泥。如下圖微信掃描,把掃描范圍限制在中間的方框內(nèi)

微信二維碼掃描

要想限制二維碼掃描區(qū)域啃匿,需要設(shè)置一個(gè)參數(shù)rectOfInterest 這個(gè)參數(shù)有點(diǎn)特別蛔外,這個(gè)參數(shù)的react 與平常設(shè)置的坐標(biāo)系是完全相反的,即X與Y互換溯乒、W與H互換冒萄。

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

設(shè)置的方法(掃描demo中已經(jīng)加入了設(shè)置掃描區(qū)域)

  [_output setRectOfInterest:CGRectMake()];

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

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

生成二維碼圖片

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

在這里借鑒的有別人的文章迎献,想看原文點(diǎn)這里

首先LCQrcodeUtil.h中
/**
 *  生成二維碼圖片
 *
 *  @param QRString  二維碼內(nèi)容
 *  @param sizeWidth 圖片size(正方形)
 *  @param color     填充色
 *
 *  @return  二維碼圖片
 */
+(UIImage *)createQRimageString:(NSString *)QRString sizeWidth:(CGFloat)sizeWidth fillColor:(UIColor *)color;
LCQrcodeUtil.m中
#pragma mark 生成二維碼
/**
 *  生成二維碼圖片
 *
 *  @param QRString  二維碼內(nèi)容
 *  @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;
}

解析二維碼圖片

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

首先LCQrcodeUtil.h中
/**
 *  讀取圖片中二維碼信息
 *
 *  @param image 圖片
 *
 *  @return 二維碼內(nèi)容
 */
+(NSString *)readQRCodeFromImage:(UIImage *)image;
LCQrcodeUtil.m中
#pragma mark 讀取圖片二維碼
/**
 *  讀取圖片中二維碼信息
 *
 *  @param image 圖片
 *
 *  @return 二維碼內(nèi)容
 */
+(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;
}

}

都是直接封裝成類方法調(diào)用腻贰,這個(gè)類我也已經(jīng)上傳到git吁恍,不想CV的可以到git中下載,地址:
https://github.com/liutongchao/LCQRCodeUtil

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末播演,一起剝皮案震驚了整個(gè)濱河市冀瓦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌写烤,老刑警劉巖翼闽,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異洲炊,居然都是意外死亡感局,警方通過查閱死者的電腦和手機(jī)尼啡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來询微,“玉大人崖瞭,你說我怎么就攤上這事〕琶” “怎么了书聚?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長代态。 經(jīng)常有香客問我寺惫,道長,這世上最難降的妖魔是什么蹦疑? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任西雀,我火速辦了婚禮,結(jié)果婚禮上歉摧,老公的妹妹穿的比我還像新娘艇肴。我一直安慰自己,他們只是感情好叁温,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布再悼。 她就那樣靜靜地躺著,像睡著了一般膝但。 火紅的嫁衣襯著肌膚如雪冲九。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天跟束,我揣著相機(jī)與錄音莺奸,去河邊找鬼。 笑死冀宴,一個(gè)胖子當(dāng)著我的面吹牛灭贷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播略贮,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼甚疟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了逃延?” 一聲冷哼從身側(cè)響起览妖,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎真友,沒想到半個(gè)月后黄痪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盔然,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年桅打,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愈案。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挺尾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出站绪,到底是詐尸還是另有隱情遭铺,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布恢准,位于F島的核電站魂挂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏馁筐。R本人自食惡果不足惜涂召,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望敏沉。 院中可真熱鬧果正,春花似錦、人聲如沸盟迟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽攒菠。三九已至迫皱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間辖众,已是汗流浹背卓起。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赵辕,地道東北人既绩。 一個(gè)月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像还惠,于是被迫代替她去往敵國和親饲握。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容