iOS封装二维码的扫描和生成,可直接使用

二维码可以很方便的分享一些信息而广泛使用,很多app也就有了生成二维码和扫瞄二维码的需求了,所以用到的地方不少, 再正常不过的一个功能了,早期开发的时候可能使用过ZBar或者ZXing这两个相对古老的第三方,在苹果没有开发原生二维码扫描和生成的时候,这两个第三方也给我们带来了很多方便,对于现在这两个使用的频率也不高了,因为苹果原生的意见有很高的效率了。

效果实现

二维码的生成
  • 不带Logo图标的二维码
    -> 导入 #import "JYErWeiCode.h" 头文件
 JYErWeiCode *erWeiCode = [[JYErWeiCode alloc] init];
    
    erWeiCode.source = @"http://www.jianshu.com/p/47bcb0422c4a";   // 设置二维码存储的资源信息
    //erWeiCode.erWeiCodeColor = setColor(56, 34, 230);    // 设置二维码的颜色,默认是黑白
   
    self.imgView.image = [erWeiCode erWeiCode];    // 生成一张高清的二维码图片,显示出来
默认生成二维码黑白颜色.png
erWeiCode.marginColor = [UIColor cyanColor];      // 自定义设置二维码的颜色
自定义二维码颜色效果.png
  • 带Logo图标的二维码(基本上带有logo图片都会设置圆角)
JYErWeiCode *erWeiCode = [[JYErWeiCode alloc] init];
    
    erWeiCode.source = @"http://www.jianshu.com/p/47bcb0422c4a";
    erWeiCode.logo = @"IMG_6396";                      // logo图片资源
    erWeiCode.rounde = 20.0f;                          // 图片圆角大小
    erWeiCode.margin = 4.0f;                           // 外边框的宽度
    erWeiCode.erWeiCodeColor = [UIColor cyanColor];
    erWeiCode.marginColor = [UIColor cyanColor];       // 外边框的颜色
    
    self.imgView.image = [erWeiCode erWeiCode];
带logo图标的二维码.png

!注意:在自定义的时候logo图片的大小不能过大,过大会遮住一下二维码的扫描位置从而影响二维码的扫描

二维码的扫描

-> 在控制器导入#import "JYScanErWeiCode.h" 、#import "JYPreviewView.h"
1.创建一个相机捕获会话的view

- (JYPreviewView *)previewView
{
    if (!_previewView) {
        
        _previewView = [[JYPreviewView alloc] init];
        _previewView.cornerColor = [UIColor cyanColor];    // 设置四个角边框的颜色
        
        [self.view addSubview:_previewView];
    }
    return _previewView;
}

2.加载二维码扫描

// 懒加载二维码扫描
- (JYScanErWeiCode *)scanErWeiCode
{
    if (!_scanErWeiCode) {
        
        _scanErWeiCode = [[JYScanErWeiCode alloc] initWithPreviewView:self.previewView];
        
        _scanErWeiCode.delegate = self;
    }
    return _scanErWeiCode;
}

3.开启相机扫描

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    [self.scanErWeiCode startSession];
}

4.当控制器消耗时关闭相机

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
    [self.scanErWeiCode stopSession];
}

5.设置JYScanErWeiCodeDelegate的代理方法

- (void)erWeiCodeScanStringValue:(NSString *)str
{
    NSLog(@"扫描结果:%@", str);
    JYWebController *webCtl = [[JYWebController alloc] init];
    webCtl.urlStr = str;
    
    [self.navigationController pushViewController:webCtl animated:YES];
}

效果展示

GIF.gif

具体代码实现

-> source 在创建生成二维码的对象时,一定要有值

- (UIImage *)erWeiCode
{
    if (self.source) {
        // 1.生成清晰的二维码图片
        UIImage *imgQRCode = [self createQRCodeImage:self.source];
        
        // 2.设置二维码的颜色 (默认是黑色)
        imgQRCode = [JYErWeiCode specialColorImage:imgQRCode color:self.erWeiCodeColor];
        
        // 3.生成一张有圆角外边框的logo图片
        UIImage *logoImg = [self createRoundeLogo];
        
        if (logoImg) {   // 如果需要设置logo图片
            // 4.合成一张有logo图标的二维码
            UIImage *needImg = [self addIconToQRCodeImage:imgQRCode icon:logoImg];
            
            return needImg;
            
        } else      // 不需要设置logo图片
        {
            return imgQRCode;
        }
    } else
    {
        NSLog(@"Error: source= nil,你创建了一个空的二维码,请设置二维码的内容");
        return nil;
    }
}

- (UIImage *)createRoundeLogo
{
    if (self.logo.length) {
        if (self.rounde > 0) {
            // 1.生成一张圆角logo图片
            UIImage *logoImg = [self imageRoundeImage:[UIImage imageNamed:self.logo] rounde:self.rounde];
            
            if (self.margin > 0) {
                // 2.生成一张有外边框的logo图片 (默认外边框颜色为白色)
                UIColor *marginColor = (self.marginColor == NULL) ? [UIColor whiteColor] : self.marginColor;
                UIImage *marginImg = [self imageAddColorBorder:logoImg color:marginColor margin:self.margin];
                
                // 3.设置有外边框logo图片的圆角
                UIImage *mLogo = [self imageRoundeImage:marginImg rounde:self.rounde];
                
                return mLogo;
            } else {
                return logoImg;
            }
        } else {
            return [UIImage imageNamed:self.logo];
        }
    } else {
        return nil;
    }
}

对图片处理的操作

描述:使用iOS 7后的CIFilter对象操作,生成二维码图片imgQRCode(会拉伸图片,比较模糊,效果不佳, 使用核心绘图框架CG(Core Graphics)对象操作,进一步针对大小生成二维码图片imgAdaptiveQRCode(图片大小适合,清晰,效果好)
source: 二维码中的数据可以是字符串和URL两种类型, 如果我们想要生成URL的二维码, 只需要把字符串替换为一个URL字符串即可
rate: 越大,越清晰(未提供接口,默认50.0, 如果需要更高清的, 可修改这个值)

- (UIImage *)createQRCodeImage:(NSString *)source
{
    // 1. 给滤镜添加数据
    NSData *data = [source dataUsingEncoding:NSUTF8StringEncoding];
    
    // 2. 创建一个二维码滤镜实例(CIFilter)
    CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    [filter setValue:data forKey:@"inputMessage"];
    [filter setValue:@"H" forKey:@"inputCorrectionLevel"];
    
    // 3.生成一张清晰度不高的二维码
    UIImage *image = [UIImage imageWithCIImage:filter.outputImage
                                         scale:1.0
                                   orientation:UIImageOrientationUp];
    
    // 4.对图片做处理, 使图片大小合适,清晰,效果好
    CGFloat width = image.size.width * 50.0;
    CGFloat height = image.size.height * 50.0;
    
    UIGraphicsBeginImageContext(CGSizeMake(width, height));
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetInterpolationQuality(context, kCGInterpolationNone);
    [image drawInRect:CGRectMake(0, 0, width, height)];
    UIImage *needImg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return  needImg;
}

默认产生的黑白色的二维码图片;我们可以让它产生其它颜色的二维码图片,例如:蓝白色的二维码图片
color: 设置二维码的颜色

+ (UIImage *)specialColorImage:(UIImage *)image color:(UIColor *)color
{
    CGFloat components[3];
    [JYErWeiCode getRGBComponents:components forColor:color];
    
    const CGFloat imageW = image.size.width;
    const CGFloat imageH = image.size.height;
    
    size_t bytesPerRow = imageW * 4;
    uint32_t *rgbImageBuf = (uint32_t *)malloc(bytesPerRow *imageH);
    
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    
    CGContextRef contextRef = CGBitmapContextCreate(rgbImageBuf, imageW, imageH, 8, bytesPerRow, colorSpaceRef, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
    
    CGContextDrawImage(contextRef, CGRectMake(0, 0, imageW, imageH), image.CGImage);
    CGFloat pixelNum = imageW * imageH;
    uint32_t *pCurPtr = rgbImageBuf;
    for (int i = 0; i < pixelNum; i ++, pCurPtr ++) {
        if ((*pCurPtr & 0xFFFFFF00) < 0x99999900) {
            
            uint8_t *ptr = (uint8_t *)pCurPtr;
            ptr[3] = components[0];
            ptr[2] = components[1];
            ptr[1] = components[2];
        } else {
            uint8_t *ptr = (uint8_t *)pCurPtr;
            ptr[0] = 0;
        }
    }
    
    CGDataProviderRef dataProviderRef = CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow * imageH, preoviderReleaseData);
    
    CGImageRef imageRef = CGImageCreate(imageW, imageH, 8, 32, bytesPerRow, colorSpaceRef, kCGImageAlphaLast | kCGBitmapByteOrder32Little, dataProviderRef, NULL, true, kCGRenderingIntentDefault);
    CGDataProviderRelease(dataProviderRef);
    
    UIImage *img = [UIImage imageWithCGImage:imageRef];
    
    CGImageRelease(imageRef);
    CGContextRelease(contextRef);
    CGColorSpaceRelease(colorSpaceRef);
    
    return img;
}

设置图片的圆角
rounde: 设置圆角大小
注明:在这设置的图片在圆角周围有羽化的现象,不知道啥情况,有大神知道可留言一下,谢谢

- (UIImage *)imageRoundeImage:(UIImage *)image rounde:(CGFloat)rounde
{
    CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
    
    UIGraphicsBeginImageContextWithOptions(image.size, NO, 1.0);
    
    [UIBezierPath bezierPathWithRect:rect];
    [[UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, image.size.width, image.size.height) cornerRadius:rounde] addClip];
    
    [image drawInRect:rect];
    
    UIImage *needImg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return needImg;
}

给图片添加不同颜色的边框
color: 外边框颜色
margin: 外边框大小

- (UIImage *)imageAddColorBorder:(UIImage *)image color:(UIColor *)color margin:(CGFloat)margin
{
    CGFloat imageW = image.size.width;
    CGFloat imageH = image.size.height;
    CGFloat colorW = imageW + margin * 2;
    CGFloat colorH = imageH + margin * 2;
    
    // 用颜色来转换成一张图片
    UIImage *colorImg = [JYErWeiCode imageWithColor:color size:CGSizeMake(colorW, colorH)];
    
    UIGraphicsBeginImageContext(colorImg.size);
    
    [colorImg drawInRect:CGRectMake(0, 0, colorW, colorH)];
    [image drawInRect:CGRectMake(margin, margin, imageW, imageH)];
    
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    
    UIGraphicsEndImageContext();
    
    return img;
}

两张图片合成一张
image: 二维码
icon: 中间Logo图标

- (UIImage *)addIconToQRCodeImage:(UIImage *)image icon:(UIImage *)icon
{
    UIGraphicsBeginImageContext(image.size);
    
    CGFloat imageW = image.size.width;
    CGFloat imageH = image.size.height;
    CGFloat iconW = imageW * 0.25;
    CGFloat iconH = imageH * 0.25;
    
    [image drawInRect:CGRectMake(0, 0, imageW, imageH)];
    [icon drawInRect:CGRectMake((imageW - iconW) * 0.5, (imageH - iconH) * 0.5, iconW, iconH)];
    
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    
    UIGraphicsEndImageContext();
    
    return img;
}

扫描代码实现

  • 初始化相机会话 (一开始最好判断有没有相机权限,没有的话跳转到设置中设置允许使用相机,当前没有做着个处理)
- (void)initSession
{
    self.captureSession = [[AVCaptureSession alloc] init];
    
    // Setup the preview view.
    self.previewView.session = self.captureSession;
    
    // 与此队列中的会话和其他会话对象进行通信
    self.sessionQueue = dispatch_queue_create( "session queue", DISPATCH_QUEUE_SERIAL );
    
    // 设置捕追会话
    // 一般来说是不安全的 AVCaptureSession 突变 或 其任何输入,输出,或连接多个线程,在同一时间
    dispatch_async( self.sessionQueue, ^{
        
        NSError *error = nil;
        AVCaptureDevice *videoDevice = [JYScanErWeiCode deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionBack];
        AVCaptureDeviceInput *videoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
        
        if ( ! videoDeviceInput ) {
            NSLog( @"Could not create video device input: %@", error );
        }
        
        [self.captureSession beginConfiguration];
        
        if ( [self.captureSession canAddInput:videoDeviceInput] ) {
            [self.captureSession addInput:videoDeviceInput];
            self.deviceInput = videoDeviceInput;
            
            AVCaptureDevice *currentVideoDevice = self.deviceInput.device;
            self.captureDevice = currentVideoDevice;
        } else
        {
            // 无法将视频设备输入到会话中
            NSLog( @"Could not add video device input to the session" );
        }
        
        dispatch_async( dispatch_get_main_queue(), ^{
            
            AVCaptureVideoPreviewLayer *previewLayer = (AVCaptureVideoPreviewLayer *)self.previewView.layer;
            previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
        });
        
        
        self.metadataOutput = [[AVCaptureMetadataOutput alloc]init];
        [self.metadataOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
        
        if ([self.captureSession canAddOutput:self.metadataOutput]) {
            [self.captureSession addOutput:self.metadataOutput];
        }
        
        // 条码类型 AVMetadataObjectTypeQRCode
        self.metadataOutput.metadataObjectTypes = @[AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code];
        
        [self.captureSession commitConfiguration];
    });
}
  • 设置AVCaptureMetadataOutput代理
  • AVCaptureMetadataOutputObjectsDelegate获取扫描的信息
#pragma mark AVCaptureMetadataOutputObjectsDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    NSString *stringValue;
    
    if ([metadataObjects count] >0){
        AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjects objectAtIndex:0];
        stringValue = metadataObject.stringValue;
        
        if (self.delegate && [self.delegate respondsToSelector:@selector(erWeiCodeScanStringValue:)]) {
            [self.delegate erWeiCodeScanStringValue:stringValue];
        }
        [self stopSession];
    }
}

Demo代码还还有完善好,等完善好在上传

推荐阅读更多精彩内容