这段时间忙着交接工作,找工作,找房子,入职,杂七杂八的,差不多一个月没有静下来学习了.这周末晚上等外卖的时间学习一下二维码的制作与扫描.
项目采用OC语言,只要使用iOS自带的CoreImage框架,通过滤镜CIFilter生成二维码,扫描使用原生自带相机实现.
开撸:
先写一个类,封装把string转换我image和把CIImage转换为string:
QRImage.h
//
// QRImage.h
// QRcode
//
// Created by Shaoting Zhou on 2017/11/19.
// Copyright © 2017年 Shaoting Zhou. All rights reserved.
// #import <Foundation/Foundation.h>
#import <CoreImage/CoreImage.h>
#import <UIKit/UIKit.h> @interface QRImage : NSObject + (UIImage *)imageWithQRString:(NSString *)string; //把string转换我image
+ (NSString *)stringFromCiImage:(CIImage *)ciimage; //把CIImage转换为string @end
QRImage.m
//
// QRImage.m
// QRcode
//
// Created by Shaoting Zhou on 2017/11/19.
// Copyright © 2017年 Shaoting Zhou. All rights reserved.
// #import "QRImage.h" @implementation QRImage #pragma mark - 把string转换为Image
+ (UIImage *)imageWithQRString:(NSString *)string{
NSData * stringData = [string dataUsingEncoding:NSUTF8StringEncoding]; CIFilter * qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"]; //过滤器
[qrFilter setValue:stringData forKey:@"inputMessage"];
[qrFilter setValue:@"M" forKey:@"inputCorrectionLevel"]; //纠错等级
UIImage * image = [self createUIImageFromCIImage:qrFilter.outputImage withSize:];
return image;
} #pragma mark - CIImgae -> UIImage
+ (UIImage *)createUIImageFromCIImage:(CIImage *)image withSize:(CGFloat)size{
CGRect extent = CGRectIntegral(image.extent);
CGFloat scale = MIN(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent)); //1.创建bitmap;
size_t width = CGRectGetWidth(extent) * scale;
size_t height = CGRectGetHeight(extent) * scale;
CGColorSpaceRef cs = CGColorSpaceCreateDeviceGray();
CGContextRef bitmapRef = CGBitmapContextCreate(nil, width, height, , , 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); //2.保存bitmap到图片
CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
CGContextRelease(bitmapRef);
CGImageRelease(bitmapImage);
return [UIImage imageWithCGImage:scaledImage]; } #pragma mark - 把image转换为string
+ (NSString *)stringFromCiImage:(CIImage *)ciimage{
NSString * content = nil;
if(!ciimage){
return content;
}
CIDetector * detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:[CIContext contextWithOptions:nil] options:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}];
NSArray * features = [detector featuresInImage:ciimage];
if(features.count){
for (CIFeature * feature in features) {
if([feature isKindOfClass:[CIQRCodeFeature class]]){
content = ((CIQRCodeFeature *)feature).messageString;
break;
}
}
}else{
NSLog(@"解析失败,确保硬件支持");
} return content;
}
@end
上面的代码就是关键之处.
下面,写一个界面生成二维码,通过上面写好的string转换我image,显示在屏幕之上.
MainViewController.h
//
// MainViewController.h
// QRcode
//
// Created by Shaoting Zhou on 2017/11/18.
// Copyright © 2017年 Shaoting Zhou. All rights reserved.
// #import <UIKit/UIKit.h>
#import "QRImage.h"
#import "ScanViewController.h" @interface MainViewController : UIViewController
@property (nonatomic,strong) UIImageView * qrImageView;
@property (nonatomic,strong) UITextField * textField;
@end
MainViewController.m
//
// MainViewController.m
// QRcode
//
// Created by Shaoting Zhou on 2017/11/18.
// Copyright © 2017年 Shaoting Zhou. All rights reserved.
// #import "MainViewController.h" @interface MainViewController () @end @implementation MainViewController - (void)viewDidLoad {
[super viewDidLoad];
[self setUI];
self.title = @"生成二维码"; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"扫描" style:(UIBarButtonItemStylePlain) target:self action:@selector(scangQRimage)];
} -(void)setUI{
self.textField = [[UITextField alloc]initWithFrame:CGRectMake(, , , )];
self.textField.borderStyle = UITextBorderStyleRoundedRect;
[self.view addSubview:self.textField]; UIButton * btn = [[UIButton alloc]initWithFrame:CGRectMake(, , , )];
[btn setTitle:@"生成二维码" forState:(UIControlStateNormal)];
[btn setTitleColor:[UIColor redColor] forState:(UIControlStateNormal)];
[btn addTarget:self action:@selector(makeQRcode) forControlEvents:(UIControlEventTouchUpInside)];
[self.view addSubview:btn]; self.qrImageView = [[UIImageView alloc]initWithFrame:CGRectMake(, , , )];
[self.view addSubview:self.qrImageView]; }
#pragma mark - 生成二维码
-(void)makeQRcode{
[self.textField resignFirstResponder];
UIImage * img = [QRImage imageWithQRString:self.textField.text];
self.qrImageView.image = img;
} #pragma mark - push到扫描界面
-(void)scangQRimage{
ScanViewController * scanVC = [[ScanViewController alloc]init];
[self.navigationController pushViewController:scanVC animated:NO];
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} /*
#pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/ @end
再写一个界面扫描二维码和通过相册选择二维码扫描,通过上面写好的把CIImage转换为string,扫描出二维码信息显示出来即可.
ScanViewController.h
//
// ScanViewController.h
// QRcode
//
// Created by Shaoting Zhou on 2017/11/19.
// Copyright © 2017年 Shaoting Zhou. All rights reserved.
// #import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import "QRImage.h"
@interface ScanViewController : UIViewController <AVCaptureMetadataOutputObjectsDelegate,UINavigationControllerDelegate,UIImagePickerControllerDelegate>
@property (nonatomic,strong) AVCaptureSession * session;
@property (nonatomic,strong) UITextField * textField; @end
ScanViewController.m
//
// ScanViewController.m
// QRcode
//
// Created by Shaoting Zhou on 2017/11/19.
// Copyright © 2017年 Shaoting Zhou. All rights reserved.
// #import "ScanViewController.h" @interface ScanViewController () @end @implementation ScanViewController - (void)viewDidLoad {
[super viewDidLoad];
self.title = @"扫描二维码"; self.textField = [[UITextField alloc]initWithFrame:CGRectMake(, , , )];
self.textField.borderStyle = UITextBorderStyleRoundedRect;
self.textField.userInteractionEnabled = NO;
[self.view addSubview:self.textField]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"相册" style:(UIBarButtonItemStylePlain) target:self action:@selector(presentImagePicker)]; }
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self startScan]; } #pragma mark - 开始扫描
- (void)startScan{
if([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] == AVAuthorizationStatusAuthorized || [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] == AVAuthorizationStatusNotDetermined ){
self.session = [[AVCaptureSession alloc]init];
AVCaptureDeviceInput * input = [[AVCaptureDeviceInput alloc]initWithDevice:[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo] error:nil];
if(input){
[self.session addInput:input];
} AVCaptureMetadataOutput * output = [[AVCaptureMetadataOutput alloc]init];
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
if(output){
[self.session addOutput:output];
} NSMutableArray * ary = [[NSMutableArray alloc]init];
if([output.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeQRCode]){
[ary addObject:AVMetadataObjectTypeQRCode];
}
if([output.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeEAN13Code]){
[ary addObject:AVMetadataObjectTypeEAN13Code];
}
if([output.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeEAN8Code]){
[ary addObject:AVMetadataObjectTypeEAN8Code];
}
if([output.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeCode128Code]){
[ary addObject:AVMetadataObjectTypeCode128Code];
}
output.metadataObjectTypes = ary; AVCaptureVideoPreviewLayer * layer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
layer.frame = CGRectMake((self.view.bounds.size.width - )/, , , );
[self.view.layer addSublayer:layer]; UIImageView * imageView = [[UIImageView alloc]initWithFrame:CGRectMake((self.view.bounds.size.width - )/, , , )]; [self.view addSubview:imageView];
[self.session startRunning];
}
} #pragma mark - AVCaptureMetadataOutputObjectsDelegate代理方法
- (void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
NSString * str = nil;
for (AVMetadataObject * obj in metadataObjects) {
if([obj.type isEqualToString:AVMetadataObjectTypeQRCode]){
str = [(AVMetadataMachineReadableCodeObject *)obj stringValue];
[self.session startRunning];
break;
}
}
self.textField.text = str;
} #pragma mark - UIImagePickerControllerDelegate代理方法
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{
UIImage * image = [info objectForKey:UIImagePickerControllerOriginalImage]; CIImage * ciimage = [[CIImage alloc]initWithImage:image];
NSString * str = [QRImage stringFromCiImage:ciimage];
self.textField.text = str;
[self dismissViewControllerAnimated:YES completion:nil];
} #pragma mark - 弹出相册
- (void)presentImagePicker{
UIImagePickerController * imagePicker = [[UIImagePickerController alloc]init];
imagePicker.delegate = self;
[self presentViewController:imagePicker animated:NO completion:nil];
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
} /*
#pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/ @end
github: https://github.com/pheromone/QRcode
效果如下: