題外話:由于項(xiàng)目需要集成掃描二維碼功能,所以我也特意在網(wǎng)上找了一些資料,具體大家都可以通過(guò)Google來(lái)獲取資料.這里就說(shuō)一下我的大體思路
1.話不多說(shuō),先上效果圖(第一次真機(jī)錄制,可能顯示效果不太好,但實(shí)際效果會(huì)比這個(gè)更好).
2.基本實(shí)現(xiàn)
2.1掃描二維碼功能是在蘋(píng)果原生的AVFoundation框架實(shí)現(xiàn)的,所以我們需要導(dǎo)入框架:
2.2因?yàn)槲覀冃枰_(kāi)啟蘋(píng)果的相機(jī)功能,由于蘋(píng)果的隱私保護(hù)機(jī)制,所以需要進(jìn)行授權(quán)設(shè)置,所以我們添加以下代碼:
- (void)viewDidLoad {
NSString *mediaType = AVMediaTypeVideo;
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:mediaType];
if (authStatus == AVAuthorizationStatusDenied) {//關(guān)閉系統(tǒng)權(quán)限
if (isDevice_IOS8) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"相機(jī)訪問(wèn)受限" message:@"請(qǐng)?jiān)贗Phone的\"設(shè)置->隱私->相機(jī)\"選項(xiàng)中,允許\"XMSweep\"訪問(wèn)你的照相機(jī)." preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"好" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
}]];
[alert addAction:[UIAlertAction actionWithTitle:@"去設(shè)置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
if ([self canOpenSystemSettingView]) {
[self systemSettingView];
}
}]];
[self presentViewController:alert animated:YES completion:nil];
}else {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"相機(jī)訪問(wèn)受限" message:@"請(qǐng)?jiān)贗Phone的\"設(shè)置->隱私->相機(jī)\"選項(xiàng)中,允許\"XMSweep\"訪問(wèn)你的照相機(jī)." delegate:nil cancelButtonTitle:@"好的" otherButtonTitles: nil];
[alert show];
}
return;
}
}
-(BOOL)canOpenSystemSettingView{
if (isDevice_IOS8) {
NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if ([[UIApplication sharedApplication]canOpenURL:url]) {
return YES;
}else {
return NO;
}
}else{
return NO;
}
}
-(void)systemSettingView{
if (isDevice_IOS8) {
NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if ([[UIApplication sharedApplication]canOpenURL:url]) {
[[UIApplication sharedApplication]openURL:url];
}
}
}
2.2然后我們就可以自己創(chuàng)建一個(gè)控制器來(lái)跳轉(zhuǎn)掃描二維碼功能,同時(shí)我們通過(guò)Block回調(diào)來(lái)得到掃描得到的結(jié)果.
XMSweepController *sweepVC = [[XMSweepController alloc]init];
sweepVC.view.alpha = 0;
AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
[appdelegate.window.rootViewController addChildViewController:sweepVC];
[appdelegate.window.rootViewController.view addSubview:sweepVC.view];
[sweepVC setDidRecoiveBlock:^(NSString *result) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"掃描得到的網(wǎng)址:" message:result preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
}];
[UIView animateWithDuration:0.3 animations:^{
sweepVC.view.alpha = 1;
}];
3.掃描二維碼功能代碼封裝
3.1在.h文件中(因?yàn)樵趻呙瓒S碼的時(shí)候是通過(guò)系統(tǒng)自帶的掃描器,而后面再某一個(gè)方法中我們可以獲取到掃描結(jié)果,所以這里用Block來(lái)傳值是最好的)
#import <UIKit/UIKit.h>
#define IS_VAILABLE_IOS8 ([[[UIDevice currentDevice] systemVersion] intValue] >= 8)
@interface XMSweepController : UIViewController
typedef void (^XMSweepBlock)(NSString *result);
@property(nonatomic,copy)XMSweepBlock didRecoiveBlock;
-(void)setDidRecoiveBlock:(XMSweepBlock)didRecoiveBlock;
@end
3.2在.m文件中(系統(tǒng)掃描二維碼的關(guān)鍵代碼,這里只管復(fù)制黏貼就行了,全都是系統(tǒng)方法,沒(méi)什么好深究)
#import "XMSweepController.h"
#import <AVFoundation/AVFoundation.h>
#import "Masonry.h"
@interface XMSweepController()<AVCaptureMetadataOutputObjectsDelegate,UIAlertViewDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate>
{
AVCaptureSession *session; //輸入輸出的中間橋梁
int line_tag;
UIView *highlightView;
}
@end
@implementation XMSweepController
-(void)viewDidLoad
{
[super viewDidLoad];
[self instanceDevice];
}
-(void)instanceDevice
{
[[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleLightContent animated:YES];
line_tag = 18;
//獲取攝像設(shè)備
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
//創(chuàng)建輸入流
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];
//創(chuàng)建輸出流
AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc]init];
//設(shè)置代理,在主線程刷新
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
//初始化連接對(duì)象
session = [[AVCaptureSession alloc]init];
//高質(zhì)量采集率
[session setSessionPreset:AVCaptureSessionPresetHigh];
if (input) {
[session addInput:input];
}
if (output) {
[session addOutput:output];
//設(shè)置掃碼的編碼格式
NSMutableArray *a = [[NSMutableArray alloc]init];
if ([output.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeQRCode]) {
[a addObject:AVMetadataObjectTypeQRCode];
}
if ([output.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeEAN13Code]) {
[a addObject:AVMetadataObjectTypeEAN13Code];
}
if ([output.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeEAN8Code]) {
[a addObject:AVMetadataObjectTypeEAN8Code];
}
if ([output.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeCode128Code]) {
[a addObject:AVMetadataObjectTypeCode128Code];
}
output.metadataObjectTypes = a;
}
AVCaptureVideoPreviewLayer *layer = [AVCaptureVideoPreviewLayer layerWithSession:session];
layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
layer.frame = self.view.bounds;
[self.view.layer insertSublayer:layer atIndex:0];
//創(chuàng)建掃碼頁(yè)面
[self creatPickerView];
//監(jiān)聽(tīng)掃碼狀態(tài)
[session addObserver:self forKeyPath:@"running" options:NSKeyValueObservingOptionNew context:nil];
//開(kāi)始捕獲
[session startRunning];
}
3.3而這里我們需要構(gòu)建掃碼的界面(這里我們不需要擔(dān)心控件會(huì)被遮擋的,因?yàn)橄到y(tǒng)掃碼的時(shí)候就會(huì)顯示整一個(gè)攝像頭拍攝的圖像),抽取一個(gè)方法出來(lái),構(gòu)件圖如下
代碼如下:
-(void)creatPickerView
{
//左側(cè)View
UIImageView *leftView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 30, self.view.frame.size.height)];
leftView.alpha = 0.5;
leftView.backgroundColor = [UIColor blackColor];
[self.view addSubview:leftView];
//右側(cè)View
UIImageView *rightView = [[UIImageView alloc]initWithFrame:CGRectMake(self.view.frame.size.width - 30, 0, 30, self.view.frame.size.height)];
rightView.alpha = 0.5;
rightView.backgroundColor = [UIColor blackColor];
[self.view addSubview:rightView];
//上部View
UIImageView *topView = [[UIImageView alloc]initWithFrame:CGRectMake(30, 0, self.view.frame.size.width - 60, (self.view.center.y - (self.view.frame.size.width - 60) / 2))];
topView.alpha = 0.5;
topView.backgroundColor = [UIColor blackColor];
[self.view addSubview:topView];
//底部View
UIImageView *bottomView = [[UIImageView alloc]initWithFrame:CGRectMake(30, self.view.center.y + (self.view.frame.size.width - 60)/2, self.view.frame.size.width - 60, self.view.frame.size.height - ((self.view.center.y - (self.view.frame.size.width - 60)/2)))];
bottomView.backgroundColor = [UIColor blackColor];
bottomView.alpha = 0.5;
[self.view addSubview:bottomView];
//掃描框
UIImageView *centerView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width - 60, self.view.frame.size.width)];
centerView.center = self.view.center;
centerView.backgroundColor = [UIColor clearColor];
centerView.image = [UIImage imageNamed:@"掃描框"];
centerView.contentMode = UIViewContentModeScaleAspectFit;
[self.view addSubview:centerView];
//掃描線
UIImageView *lineView = [[UIImageView alloc]initWithFrame:CGRectMake(30, CGRectGetMaxY(topView.frame), self.view.frame.size.width - 60, 2)];
lineView.tag = line_tag;
lineView.image = [UIImage imageNamed:@"掃描線"];
lineView.backgroundColor = [UIColor clearColor];
lineView.contentMode = UIViewContentModeScaleAspectFill;
[self.view addSubview:lineView];
//文字
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(30, CGRectGetMinY(bottomView.frame), self.view.frame.size.width - 60, 60)];
label.textAlignment = NSTextAlignmentCenter;
label.font = [UIFont systemFontOfSize:16];
label.textColor = [UIColor whiteColor];
label.text = @"將二維碼放入框內(nèi),即可自動(dòng)掃描";
label.backgroundColor = [UIColor clearColor];
[self.view addSubview:label];
//返回按鈕
UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
backBtn.frame = CGRectMake(-2, 10, 60, 64);
[backBtn addTarget:self action:@selector(backToView:) forControlEvents:UIControlEventTouchUpInside];
[backBtn setImage:[UIImage imageNamed:@"白色返回_想去"] forState:UIControlStateNormal];
[self.view addSubview:backBtn];
UIImageView *imageV = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"erweima@3x"]];
imageV.userInteractionEnabled = YES;
// imageV.frame = CGRectMake(280, 10, 35, 35);
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(chooseImage)];
[imageV addGestureRecognizer:tap];
[self.view addSubview:imageV];
[imageV mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view).with.offset(25);
make.right.equalTo(self.view).with.offset(-15);
make.size.mas_equalTo(CGSizeMake(35, 35));
}];
}
3.4然后就是添加動(dòng)畫(huà)效果了(掃碼的時(shí)候掃描線由上到下地移動(dòng)).我們可以在系統(tǒng)監(jiān)聽(tīng)掃碼狀態(tài)的代理方法寫(xiě).
//監(jiān)聽(tīng)掃碼狀態(tài),添加掃碼動(dòng)畫(huà)
-(void)setDidRecoiveBlock:(XMSweepBlock)didRecoiveBlock
{
_didRecoiveBlock = [didRecoiveBlock copy];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
if ([object isKindOfClass:[AVCaptureSession class]]) {
BOOL isRunning = ((AVCaptureSession *)object).isRunning;
if (isRunning) {
//添加動(dòng)畫(huà)
[self addAnimation];
}else{
//移除動(dòng)畫(huà)
[self removeAnimation];
}
}
}
//添加掃碼動(dòng)畫(huà)
-(void)addAnimation{
UIView *line = [self.view viewWithTag:line_tag];
line.hidden = NO;
CABasicAnimation *animation = [self moveTime:2 fromY:[NSNumber numberWithFloat:0] toY:[NSNumber numberWithFloat:self.view.frame.size.width - 60 -2] rep:OPEN_MAX];
[line.layer addAnimation:animation forKey:@"lineAnimation"];
}
//移除掃碼動(dòng)畫(huà)
-(void)removeAnimation{
UIView *line = [self.view viewWithTag:line_tag];
[line.layer removeAnimationForKey:@"lineAnimation"];
line.hidden = YES;
}
-(CABasicAnimation *)moveTime:(float)time fromY:(NSNumber *)fromY toY:(NSNumber *)toY rep:(int)rep
{
CABasicAnimation *anima = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
[anima setFromValue:fromY];
[anima setToValue:toY];
anima.duration = time;
anima.delegate = self;
anima.repeatCount = rep;
//動(dòng)畫(huà)結(jié)束的時(shí)候,保持動(dòng)畫(huà)的最后狀態(tài)
anima.fillMode = kCAFillModeForwards;
anima.removedOnCompletion = NO;
//控制動(dòng)畫(huà)的速度
anima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
return anima;
}
3.5這里我們通過(guò)前面定義的Block,設(shè)置相關(guān)的Block方法(獲取到相對(duì)應(yīng)的值),我們?cè)趻叽a得到結(jié)果后,退出當(dāng)前的控制器,且停止系統(tǒng)捕獲信息的方法.
//獲取到掃碼結(jié)果
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
if (metadataObjects.count > 0) {
[session stopRunning];
AVMetadataMachineReadableCodeObject *metadataObject = [metadataObjects objectAtIndex:0];
//輸出掃描字符串
NSString *data = metadataObject.stringValue;
NSLog(@"我想要得到的數(shù)據(jù)是%@",data);
if (_didRecoiveBlock) {
_didRecoiveBlock(data);
[self removeFromSuperview];
}
}
}
//從父視圖移除
-(void)removeFromSuperview
{
[session removeObserver:self forKeyPath:@"running" context:nil];
[UIView animateWithDuration:0.3 animations:^{
self.view.alpha = 0;
} completion:^(BOOL finished) {
[self.view removeFromSuperview];
[self removeFromParentViewController];
}];
[[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleDefault animated:YES];
}
4.本地二維碼掃碼功能
4.1其實(shí)實(shí)現(xiàn)原理很簡(jiǎn)單,也是從相冊(cè)選中二維碼照片(單張選取和選中),然后通過(guò)系統(tǒng)的方法獲取掃碼的結(jié)果,最后也是通過(guò)Block來(lái)回調(diào).
-(void)chooseImage
{
UIImagePickerController *picker = [[UIImagePickerController alloc]init];
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
picker.delegate = self;
picker.allowsEditing = YES;
[self presentViewController:picker animated:YES completion:nil];
}
//選中單張照片
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
[picker dismissViewControllerAnimated:YES completion:nil];
__block UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];
if (!image) {
image = [info objectForKey:UIImagePickerControllerOriginalImage];
}
//系統(tǒng)自帶的識(shí)別方法
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{ CIDetectorAccuracy : CIDetectorAccuracyHigh }];
CGImageRef ref = (CGImageRef)image.CGImage;
CIImage *cii = [CIImage imageWithCGImage:ref];
NSArray *feacture = [detector featuresInImage:cii];
if (feacture.count >= 1) {
CIQRCodeFeature *feature = [feacture objectAtIndex:0];
NSString *scanResult = feature.messageString;
if (_didRecoiveBlock) {
self.didRecoiveBlock(scanResult);
[self selfRemoveFromSuperview];
} else {
if (IS_VAILABLE_IOS8) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"掃碼" message:scanResult preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"好" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
[session startRunning];
}]];
[self presentViewController:alert animated:YES completion:nil];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"掃碼" message:scanResult delegate:self cancelButtonTitle:@"好" otherButtonTitles:nil];
[alert show];
}
}
}
}
- (void)selfRemoveFromSuperview{
[session removeObserver:self forKeyPath:@"running" context:nil];
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.view.alpha = 0;
} completion:^(BOOL finished) {
[self.view removeFromSuperview];
[self removeFromParentViewController];
}];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:YES];
}
寫(xiě)到這里,基本功能都能實(shí)現(xiàn),具體可以根據(jù)自己的需求來(lái)修改.
源碼下載,如果能對(duì)你有用的話,希望能能給個(gè)Star!(__) 嘻嘻……