开篇
二维码在软件中的应用算是比较普遍的,常见的比如生成二维码名片,扫码支付,扫码生成邀请码等一系列操作。最近一直在完善扫码部分的代码和页面逻辑,今天有空就一起整理一下整个流程相关的东西吧。
主要介绍内容
生成二维码
商家端的生成二维码供他人扫码付款
生成自己的邀请二维码供他人扫码注册
生成后的二维码状态刷新二维码扫描
扫码页面的布局,二维码扫描框等所在页面的处理
调用相机扫描二维码
扫码后的判断来进行逻辑跳转长按识别相册二维码
相册添加手势长按识别二维码
生成二维码
这里先放两张被扫描的二维码界面,分别是未被扫描和扫描中的页面布局。
初始状态
收款页面的布局是确定的,包括付款成头像,姓名,付款状态等,会根据页面所处的状态进行显示。 下图显示付款中的状态
付款中
二维码的生成代码:
NSString *codeUrlString = [[[data objectForKey:@"data"] objectForKey:@"content"] objectForKey:@"url"];
erweima_id =[[[data objectForKey:@"data"] objectForKey:@"content"] objectForKey:@"erweima_id"];
ZXEncodeHints *hints = [ZXEncodeHints hints];
hints.encoding = NSUTF8StringEncoding;// 设置编码类型
hints.errorCorrectionLevel = [ZXQRCodeErrorCorrectionLevel errorCorrectionLevelH]; // 设置纠正级别,越高识别越快
ZXMultiFormatWriter *writer = [ZXMultiFormatWriter writer];
ZXBitMatrix* result = [writer encode:codeUrlString format:kBarcodeFormatQRCode width:500 height:500 hints:hints error:&error];
if (result) {
CGImageRef image = [[ZXImage imageWithMatrix:result] cgimage];
self.qrCodeImageView.image=[UIImage imageWithCGImage:image];
} else {
}
这里使用的ZXingObJC库生成的二维码,生成之后创建定时器进行二维码的刷新
[netRequestTimer invalidate];
netRequestTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(ScanCodeStartNetworkRequest) userInfo:nil repeats:YES];
[netRequestTimer fire];
在ScanCodeStartNetworkRequest中,进行网络请求,根据后台的返回的数据来进行二维码状态的判断,看是处在未扫码,付款中,还是付款成功等状态,同时控制界面其他控件的显示和隐藏。在付款成功后,根据后台返回参数,展示自己提供的奖励或其他界面。
注意:在页面消失后要关闭定时器。
二维码扫描
二维码扫描页面布局,确定他的界面颜色,亮色线条的上线滚动动画,透明部分的范围,边界绿色框的绘制。当然也可以用第三方来设置这个界面上的view然后进行加载。
在二维码界面中写一个block来传递必要的参数,部分代码如下
#import <UIKit/UIKit.h>
typedef void(^QRUrlBlock)(NSString *url,NSString* invite_code,BOOL isResist);
@interface QRViewController : UIViewController<UIAlertViewDelegate>
@property (nonatomic, copy) QRUrlBlock qrUrlBlock;
@end
.m文件中调用相机,导入#import <AVFoundation/AVFoundation.h>
遵循AVCaptureMetadataOutputObjectsDelegate
部分代码如下:
@interface QRViewController ()<AVCaptureMetadataOutputObjectsDelegate,QRViewDelegate>
@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;
@end
@implementation QRViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor whiteColor];
_device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
// Input
_input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil];
// Output
_output = [[AVCaptureMetadataOutput alloc]init];
[_output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
// Session
_session = [[AVCaptureSession alloc]init];
[_session setSessionPreset:AVCaptureSessionPresetHigh];
if ([_session canAddInput:self.input])
{
[_session addInput:self.input];
}
if ([_session canAddOutput:self.output])
{
[_session addOutput:self.output];
}
if([[[UIDevice currentDevice] systemVersion] floatValue]>= 7.0)
{
//判断相机是否能够使用
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if(status == AVAuthorizationStatusAuthorized) {
// authorized
_output.metadataObjectTypes =@[AVMetadataObjectTypeQRCode];
_preview =[AVCaptureVideoPreviewLayer layerWithSession:_session];
_preview.videoGravity =AVLayerVideoGravityResize;
_preview.frame =self.view.layer.bounds;
[self.view.layer insertSublayer:_preview atIndex:0];
[_session startRunning];
CGFloat displacementGap= 0.0;
if (self.qr_type == QR_Type_AddFriend) {
displacementGap = 60.0;
}
//此处创建之前 的设置扫面界面的View
// CGRect screenRect = [UIScreen mainScreen].bounds;
QRView *qrRectView = [[QRView alloc] initWithFrame:CGRectMake(0, -displacementGap, SCREEN_WIDTH, SCREEN_HEIGHT + displacementGap)];
qrRectView.transparentArea = CGSizeMake(KQRWIDTH, KQRWIDTH);
qrRectView.backgroundColor = [UIColor clearColor];
// qrRectView.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
qrRectView.delegate = self;
self.view.backgroundColor = qrRectView.backgroundColor;
[self.view addSubview:qrRectView];
// UIButton *pop = [UIButton buttonWithType:UIButtonTypeCustom];
// pop.frame = CGRectMake(20, 20, 50, 50);
// [pop setTitle:@"返回" forState:UIControlStateNormal];
// [pop addTarget:self action:@selector(pop:) forControlEvents:UIControlEventTouchUpInside];
// [self.view addSubview:pop];
//修正扫描区域
CGFloat screenHeight = self.view.frame.size.height;
CGFloat screenWidth = self.view.frame.size.width;
CGRect cropRect = CGRectMake((screenWidth - qrRectView.transparentArea.width) / 2,
(screenHeight - qrRectView.transparentArea.height) / 2,
qrRectView.transparentArea.width,
qrRectView.transparentArea.height);
[_output setRectOfInterest:CGRectMake(cropRect.origin.y / screenHeight,
cropRect.origin.x / screenWidth,
cropRect.size.height / screenHeight,
cropRect.size.width / screenWidth)];
} else if(status == AVAuthorizationStatusDenied){
// denied
UIAlertController * alertcontrol=[UIAlertController alertControllerWithTitle:@"未获得授权使用摄像头" message:@"请在ios“设置”-“隐私”-“相机”中打开!" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction * action=[UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
[self.navigationController popViewControllerAnimated:YES];
}];
[alertcontrol addAction:action];
[self presentViewController:alertcontrol animated:YES completion:nil];
return ;
} else if(status == AVAuthorizationStatusRestricted){
// restricted
return;
} else if(status == AVAuthorizationStatusNotDetermined){
// not determined
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if(granted){
} else {
return;
}
}];
}
}
在AVCaptureMetadataOutputObjectsDelegate协议方法中根据二维码的信息来进行赋值和页面跳转,部分代码如下:
#pragma mark AVCaptureMetadataOutputObjectsDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
NSString *stringValue;
if ([metadataObjects count] >0)
{
//停止扫描
[_session stopRunning];
AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjects objectAtIndex:0];
stringValue = metadataObject.stringValue;
}
//根据条件进行判断 举例如下 :
if([stringValue rangeOfString:@"erweimahzb_id"].location != NSNotFound) {
/**
* 线下扫码支付
*/
[self pop:nil];
NSDictionary* dict=[self dictionaryFromQuery:[[stringValue componentsSeparatedByString:@"?"] objectAtIndex:1] usingEncoding:NSUTF8StringEncoding];
if ([dict objectForKey:@"user_id"] && [dict objectForKey:@"erweimahzb_id"]) {
if (self.qrUrlBlock) {
//对block处理
self.qrUrlBlock([dict objectForKey:@"user_id"],[dict objectForKey:@"erweimahzb_id"],0);
}
}else {
[[[UIAlertView alloc] initWithTitle:@"温馨提示" message:@"扫码异常,请重新扫码!" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:Nil, nil] show];
}
}
通过以上思路我们设置完成二维码扫描页面后,在点击对应按钮弹出二维码扫描的点击事件里,完成block方法的实现
点击事件在已经打开相机的前提下代码处理如下:
//加载二维码扫描的Controller
QRViewController *qrVC = [[QRViewController alloc] init];
__block ProfileViewController *weakSelf = self;
qrVC.qrUrlBlock=^(NSString* parent_id,NSString* invite_code,BOOL isResist){
weakSelf.navigationController.navigationBar.hidden = YES;
if (!isResist) {
//如果不是扫描邀请注册在这里处理数据,并实现跳转逻辑
}else{
//如果是扫码注册界面在此处处理数据,并跳转注册界面
}
上述代码只是做一个简单的举例,列出了实现思路,具体的逻辑根据自己项目的不同情况可做进一步的优化。
长按识别二维码
给选择的图片添加长按手势,在手势的添加事件中进行判断,代码如下
if(gesture.state==UIGestureRecognizerStateBegan){
UIImageView*tempImageView=(UIImageView*)gesture.view;
if(tempImageView.image){
CIDetector*detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{ CIDetectorAccuracy : CIDetectorAccuracyHigh }];
NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:tempImageView.image.CGImage]];
//扫描结果
CIQRCodeFeature *feature = [features objectAtIndex:0];
NSLog(@"输出扫描内容:%@",feature.messageString);
}else if (gesture.state==UIGestureRecognizerStateEnded){
}
后记
二维码比较常用到的一些功能做了一个简单的介绍,可能有一些说的不是很恰当,希望发现的小伙伴积极指出。在接触项目的同时也自己根据模块归纳总结了一下,虽然没有具体的代码,但是主要的逻辑思路和主要部分的代码已经有了,希望做个备忘,同时对大家有所帮助。(PS:简书的鸡汤文还是一如既往的多,各种xxx看我就够了,我用XXX完成了XXX ,你XXX为什么XXX,真是够够的,希望自己能坚持下去,踏踏实实的写点技术文章积累下自己吧!加油!)