iOS開發(fā)探索-二維碼

iOS7之前挑童,開發(fā)者進行掃碼編程時,一般會借助第三方庫任连。掃描二維碼 OC 的開源庫有 ZBar和ZXing,iOS7之后例诀,系統的AVMetadataObject類中课梳,為我們提供了解析二維碼的接口。經過測試余佃,使用原生API掃描和處理的效率非常高,遠遠高于第三方庫跨算。

二維碼掃描

在開始之前最重要的一步是要先導入原生的音視頻框架即 AVFoundation.framework 爆土。
以下是筆者封裝的二維碼掃描視圖QRView

QRView.h 頭文件

#import <UIKit/UIKit.h>
@class QRView;

@protocol QRViewDelegate <NSObject>
/**
 *  代理回調掃描結果
 *
 *  @param view   掃一掃視圖
 *  @param result 掃描結果
 */
- (void)qrView:(QRView*)view ScanResult:(NSString*)result;

@end
@interface QRView : UIView

@property(nonatomic,assign)id<QRViewDelegate> delegate;

@property(nonatomic,assign,readonly)CGRect scanViewFrame;

- (void)startScan;
- (void)stopScan;
@end

QRView.m 實現文件

#import "QRView.h"
#import <AVFoundation/AVFoundation.h>

@interface QRView ()<AVCaptureMetadataOutputObjectsDelegate>

@end
@implementation QRView
{
    AVCaptureSession *_session;
    UIImageView *_scanView;
    UIImageView *_lineView;
}

- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        [self initView];
    }
    return self;
}

- (void)initView
{
    UIImage *scanImage = [UIImage imageNamed:@"scanscanBg"];
    CGFloat width = CGRectGetWidth(self.frame);
    CGFloat height = CGRectGetHeight(self.frame);

    CGFloat scanW = 200;
    CGRect scanFrame = CGRectMake(width/2.-100, height/2.-100, scanW, scanW);
    _scanViewFrame = scanFrame;


    _scanView = [[UIImageView alloc]initWithImage:scanImage];
    _scanView.backgroundColor = [UIColor clearColor];
    _scanView.frame = scanFrame;
    [self addSubview:_scanView];


    //獲取攝像設備
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    //閃光燈
    if ([device hasFlash] && [device hasTorch]) {
        [device lockForConfiguration:nil];
        [device setFlashMode:AVCaptureFlashModeAuto];
        [device setTorchMode:AVCaptureTorchModeAuto];
        [device unlockForConfiguration];
    }
    //創(chuàng)建輸入流
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];

    //創(chuàng)建輸出流
    AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc]init];
    //設置代理 刷新線程
    [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];

    output.rectOfInterest = [self rectOfInterestByScanViewRect:_scanView.frame];
    //初始化連接對象
    _session = [[AVCaptureSession alloc]init];

    //采集率
    _session.sessionPreset = AVCaptureSessionPresetHigh;
    
   if (input) {
        [_session addInput:input];
    }

    if (output) {
        [_session addOutput:output];
        //設置掃碼支持的編碼格式
    
        NSMutableArray *array = [[NSMutableArray alloc]initWithCapacity:0];
    
        if ([output.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeQRCode]) {
            [array addObject:AVMetadataObjectTypeQRCode];
        }
        if ([output.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeEAN13Code]) {
            [array addObject:AVMetadataObjectTypeEAN13Code];
        }
        if ([output.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeEAN8Code]) {
            [array addObject:AVMetadataObjectTypeEAN8Code];
        }
        if ([output.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeCode128Code]) {
            [array addObject:AVMetadataObjectTypeCode128Code];
        }
        output.metadataObjectTypes = array;
    
    }

    AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:_session];
    layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    layer.frame = self.bounds;
    [self.layer insertSublayer:layer above:0];
    [self bringSubviewToFront:_scanView];
   [self setOverView];
    [_session startRunning];
    [self loopDrawLine];


}

//設置掃描區(qū)域
- (CGRect)rectOfInterestByScanViewRect:(CGRect)rect{

    CGFloat width = CGRectGetWidth(self.frame);
    CGFloat height = CGRectGetHeight(self.frame);

    CGFloat x = (height - CGRectGetHeight(rect))/2/height;
    CGFloat y = (width - CGRectGetWidth(rect))/2/width;

    CGFloat w = CGRectGetHeight(rect)/height;
    CGFloat h = CGRectGetWidth(rect)/width;

    return CGRectMake(x, y, w, h);
}

#pragma mark - 添加模糊效果
- (void)setOverView
{
    CGFloat width = CGRectGetWidth(self.frame);
    CGFloat height = CGRectGetHeight(self.frame);

    CGFloat x = CGRectGetMinX(_scanView.frame);
    CGFloat y = CGRectGetMinY(_scanView.frame);
    CGFloat w = CGRectGetWidth(_scanView.frame);
    CGFloat h = CGRectGetHeight(_scanView.frame);

    [self creatView:CGRectMake(0, 0, width, y)];
    [self creatView:CGRectMake(0, y + h, width, height - y)];
    [self creatView:CGRectMake(0, y, x, h)];
    [self creatView:CGRectMake(x + w, y, x, h)];

}

- (void)creatView:(CGRect)rect{
    CGFloat alpha = 0.5;
    UIView *view = [[UIView alloc]initWithFrame:rect];
    view.backgroundColor = [UIColor grayColor];
    view.alpha = alpha;
    [self addSubview:view];
}

#pragma mark - 動畫
- (void)loopDrawLine {

    UIImage *lineImage = [UIImage imageNamed:@"scanLine"];

    CGFloat x = CGRectGetMinX(_scanView.frame);
    CGFloat y = CGRectGetMinY(_scanView.frame);
    CGFloat w = CGRectGetWidth(_scanView.frame);
    CGFloat h = CGRectGetHeight(_scanView.frame);

    CGRect start = CGRectMake(x, y, w, 2);
    CGRect end = CGRectMake(x, y + h - 2, w, 2);

    if (!_lineView) {
        _lineView = [[UIImageView alloc]initWithImage:lineImage];
        _lineView.frame = start;
        [self addSubview:_lineView];
    }else{
        _lineView.frame = start;
    }

    __weak typeof(self) weakSelf = self;
    [UIView animateWithDuration:2 animations:^{
        _lineView.frame = end;
    } completion:^(BOOL finished) {
        [weakSelf loopDrawLine];
    }];
}

#pragma mark - AVCaptureMetadataOutputObjectsDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    if (metadataObjects.count>0) {
        AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects firstObject];
        if ([_delegate respondsToSelector:@selector(qrView:ScanResult:)]) {
            [_delegate qrView:self ScanResult:metadataObject.stringValue];
        }
    }
}

- (void)startScan{
    _lineView.hidden = NO;
    [_session startRunning];
}

- (void)stopScan{
    _lineView.hidden = YES;
    [_session stopRunning];
}

@end

調用示例:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
QRView *qrView = [[QRView alloc]initWithFrame:self.view.bounds];
qrView.delegate = self;
[self.view addSubview:qrView];
}

//代理回調掃描結果
- (void)qrView:(QRView *)view ScanResult:(NSString *)result
{
    [view stopScan];
    NSLog(@"掃描結果:%@",result);
}
掃一掃示例圖片

生成二維碼

生成二維碼UIImage+QRCode

UIImage+QRCode.h 文件

#import <UIKit/UIKit.h>

@interface UIImage (QRCode)

+ (UIImage *)qrImageByContent:(NSString *)content;

//pre
+ (UIImage *)qrImageWithContent:(NSString *)content size:(CGFloat)size;
/**
 *   色值 0~255
 */
+ (UIImage *)qrImageWithContent:(NSString *)content size:(CGFloat)size red:(NSInteger)red green:(NSInteger)green blue:(NSInteger)blue;

+ (UIImage *)qrImageWithContent:(NSString *)content logo:(UIImage *)logo size:(CGFloat)size red:(NSInteger)red green:(NSInteger)green blue:(NSInteger)blue;

@end

UIImage+QRCode.m 文件

#import "UIImage+QRCode.h"
#import <AVFoundation/AVFoundation.h>

@implementation UIImage (QRCode)

void ProviderReleaseData (void *info, const void *data, size_t size){
    free((void*)data);
}

+ (CIImage *)qrByContent:(NSString *)content {
    CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    NSData *stringData = [content dataUsingEncoding:NSUTF8StringEncoding];
    [qrFilter setValue:stringData forKey:@"inputMessage"];
    [qrFilter setValue:@"H" forKey:@"inputCorrectionLevel"];
    CIImage *image = qrFilter.outputImage;
    return image;
}

+ (UIImage *)qrImageWithContent:(NSString *)content size:(CGFloat)size  {
    CIImage *image = [UIImage qrByContent:content];

    CGRect extent = CGRectIntegral(image.extent);
    CGFloat scale = MIN(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent));
    // 創(chuàng)建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];
}

+ (UIImage *)qrImageWithContent:(NSString *)content size:(CGFloat)size red:(NSInteger)red green:(NSInteger)green blue:(NSInteger)blue {
    UIImage *image = [UIImage qrImageWithContent:content size:size];
    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, 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;
}

+ (UIImage *)qrImageWithContent:(NSString *)content logo:(UIImage *)logo size:(CGFloat)size red:(NSInteger)red green:(NSInteger)green blue:(NSInteger)blue {
    UIImage* resultUIImage = [self qrImageWithContent:content size:size red:red green:green blue:blue];

    // 添加logo
    CGFloat logoW = resultUIImage.size.width / 5.;
    CGRect logoFrame = CGRectMake(logoW * 2, logoW * 2, logoW, logoW);
    UIGraphicsBeginImageContext(resultUIImage.size);
    [resultUIImage drawInRect:CGRectMake(0, 0, resultUIImage.size.width, resultUIImage.size.height)];
    [logo drawInRect:logoFrame];
    UIImage *kk = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return kk;
}



+ (UIImage *)qrImageByContent:(NSString *)content {
    NSData *stringData = [content dataUsingEncoding:NSUTF8StringEncoding];

    CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    [qrFilter setValue:stringData forKey:@"inputMessage"];
    [qrFilter setValue:@"M" forKey:@"inputCorrectionLevel"];
  
    CIImage *qrImage = qrFilter.outputImage;

    CGSize size = CGSizeMake(300, 300);
    CGImageRef cgImage = [[CIContext contextWithOptions:nil] createCGImage:qrImage fromRect:qrImage.extent];
    UIGraphicsBeginImageContext(size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetInterpolationQuality(context, kCGInterpolationNone);
    CGContextScaleCTM(context, 1, -1);
    CGContextDrawImage(context, CGContextGetClipBoundingBox(context), cgImage);
    UIImage *codeImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    CGImageRelease(cgImage);

   return codeImage;
}

@end

調用示例:

UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectZero];
imgView.frame = CGRectMake(0, 0, 200, 200);
imgView.center = self.view.center;
imgView.layer.shadowColor = [UIColor blackColor].CGColor;
imgView.layer.shadowOffset = CGSizeMake(1, 2);
imgView.layer.shadowRadius = 1;
imgView.layer.shadowOpacity = 0.5;
[self.view addSubview:imgView];
imgView.image = [UIImage qrImageByContent:@"www.baidu.com"];
生成的二維碼示例

項目地址:https://github.com/524429264/QRCodeDemo

掃描下方二維碼關注我###

零距離仰望星空
在此感謝各位讀者的來訪,您的關注是我寫作分享的最大動力诸蚕。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末步势,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子背犯,更是在濱河造成了極大的恐慌坏瘩,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漠魏,死亡現場離奇詭異倔矾,居然都是意外死亡,警方通過查閱死者的電腦和手機柱锹,發(fā)現死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門哪自,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人禁熏,你說我怎么就攤上這事壤巷。” “怎么了瞧毙?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵胧华,是天一觀的道長寄症。 經常有香客問我,道長矩动,這世上最難降的妖魔是什么有巧? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮铅忿,結果婚禮上剪决,老公的妹妹穿的比我還像新娘。我一直安慰自己檀训,他們只是感情好柑潦,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著峻凫,像睡著了一般渗鬼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上荧琼,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天譬胎,我揣著相機與錄音,去河邊找鬼命锄。 笑死堰乔,一個胖子當著我的面吹牛,可吹牛的內容都是我干的脐恩。 我是一名探鬼主播镐侯,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼驶冒!你這毒婦竟也來了苟翻?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤骗污,失蹤者是張志新(化名)和其女友劉穎崇猫,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體需忿,經...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡诅炉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了屋厘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汞扎。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖擅这,靈堂內的尸體忽然破棺而出澈魄,到底是詐尸還是另有隱情,我是刑警寧澤仲翎,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布痹扇,位于F島的核電站铛漓,受9級特大地震影響,放射性物質發(fā)生泄漏鲫构。R本人自食惡果不足惜浓恶,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望结笨。 院中可真熱鬧包晰,春花似錦、人聲如沸炕吸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赫模。三九已至树肃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瀑罗,已是汗流浹背胸嘴。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留斩祭,地道東北人劣像。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像摧玫,于是被迫代替她去往敵國和親驾讲。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容