关于扫描二维码的一些需求点(扫描二维码,闪光灯,相册,动画)

关于扫描二维码的一些需求点(扫描二维码,闪光灯,相册,动画)

网上相关的很多,我这里是总结下自己遇到的需求,总共有几个需求点,

  1. 识别二维码中的数据
  2. 扫码动画
  3. 打开/关闭闪光灯
  4. 取出相册第一张图片,点击可以打开相册,并识别相册二维码
    先给个gif是整体效果图,估计图片显示问题看起来有点卡...


    整体效果

打开相机开始扫描

这个需求最重要的就是打开相机扫描吧,首先先确定你的权限,以及在plist加上这些描述


plist添加描述
另外需要确认是否打开权限如果没有打开权限直接操作会崩溃的...
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if(status == AVAuthorizationStatusAuthorized) {
        // authorized
        [self setupCamera];
        [self setupLatestAsset];
    } else {
     // alert :@"Tips" message:@"Authorization is required to use the camera, please check your permission settings: Settings> Privacy> Camera" 
    }

1. 识别二维码中的数据

这里需要导入#import <AVFoundation/AVFoundation.h>主要会用到的有下面的

@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;

其中AVCaptureDevice是设备,AVCaptureDeviceInput是输入,我的理解就是摄像头的一些设置,AVCaptureMetadataOutput是输出,数据输出,AVCaptureSession我的理解是事务,就是整个流程的事务,AVCaptureVideoPreviewLayer是扫描画面的图层.懒加载如下

#pragma mark - lazyMethod
- (AVCaptureDevice *)device
{
    if (!_device) {
        _device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    }
    return _device;
}
- (AVCaptureDeviceInput *)input
{
    if (!_input) {
        _input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil];
    }
    return _input;
}
- (AVCaptureMetadataOutput *)output
{
    if (!_output) {
        _output = [[AVCaptureMetadataOutput alloc] init];
        
        
    }
    return _output;
}
- (AVCaptureSession *)session
{
    if (!_session) {
        _session = [[AVCaptureSession alloc] init];
        [_session setSessionPreset:AVCaptureSessionPresetHigh];
    }
    return _session;
}
- (AVCaptureVideoPreviewLayer *)preview
{
    if (!_preview) {
        _preview = [AVCaptureVideoPreviewLayer layerWithSession:_session];
        _preview.videoGravity = AVLayerVideoGravityResizeAspectFill;
        _preview.frame =self.view.layer.bounds;
    }
    return _preview;
}

接下来就是上述属性的设置

    // 链接输入输出
    if ([self.session canAddInput:self.input]){
        
        [self.session addInput:self.input];
    }
    
    // http://stackoverflow.com/questions/31063846/avcapturemetadataoutput-setmetadataobjecttypes-unsupported-type-found
    if ([self.session canAddOutput:self.output]){
        
        [self.session addOutput:self.output];
        [self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
        // 设置条码类型
        self.output.metadataObjectTypes =@[AVMetadataObjectTypeQRCode];
    }
    
    // 添加扫描画面
    [self.view.layer insertSublayer:self.preview atIndex:0];

另外需要遵守AVCaptureMetadataOutputObjectsDelegate协议,因为你要在这里拿到扫描出二维码的信息

#pragma mark - AVCaptureMetadataOutputObjectsDelegate
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    NSString *stringValue;
    if ([metadataObjects count] > 0){
        //停止扫描
        [self.session stopRunning];
        AVMetadataMachineReadableCodeObject * metadataObject = [metadataObjects objectAtIndex:0];
        stringValue = metadataObject.stringValue;
        NSLog(@"------------%@-----------",stringValue);
    }
}

到了这里其实就已经可以识别二维码了,另外还有三个小需求(动画,相册,闪光灯)

2. 扫码动画

动画这一块我并不想讲的太多,网上其实有很多,除去一些基本的设置外,就是做一个transform.translation.y的动画,给你的线加上下面这个动画就ok,很多时候都会写个kvo来决定动画开启,这里见仁见智了

- (CABasicAnimation *)moveYTime:(float)time fromY:(NSNumber *)fromY toY:(NSNumber *)toY rep:(int)rep
{
    CABasicAnimation *animationMove = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
    [animationMove setFromValue:fromY];
    [animationMove setToValue:toY];
    animationMove.duration = time;
    animationMove.delegate = self;
    animationMove.repeatCount  = rep;
    animationMove.fillMode = kCAFillModeForwards;
    animationMove.removedOnCompletion = NO;
    animationMove.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    return animationMove;
}

3. 打开/关闭闪光灯

闪光灯用到的类主要是AVCaptureDevice,还是直接贴方法

// 打开手电筒开关按钮点击事件
- (void)torchOnTouchButton:(UIButton *)sender{
    
    Class captureDeviceClass = NSClassFromString(@"AVCaptureDevice");
    if (captureDeviceClass != nil) {
        
        AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        // 判断是否有闪光灯
        if ([device hasTorch]) {
            
            // 请求独占访问硬件设备
            [device lockForConfiguration:nil];
            if (sender.tag == 0) {
                
                sender.tag = 1;
                [self.torchButton setTitle:@"关闭闪光灯" forState:UIControlStateNormal];
                [device setTorchMode:AVCaptureTorchModeOn]; // 手电筒开
            }else{
                
                sender.tag = 0;
                [self.torchButton setTitle:@"打开闪光灯" forState:UIControlStateNormal];
                [device setTorchMode:AVCaptureTorchModeOff]; // 手电筒关
            }
            // 请求解除独占访问硬件设备
            [device unlockForConfiguration];
        }
    }
}

我这里主要使用button的tag来记录是否打开情况,另外,记得在页面关闭是关了闪光灯

4. 取出相册最新图片,点击打开相册,并识别相册二维码

其实我想记录的原因就是因为最后一个需求,不然上面的其实网上都很多,主要就是去除相册最新的一张照片,网上的方法都是iOS 7,iOS 8的,在iOS 9之后都过期了.

打开相册

打开相册也是需要判断是否给了权限,万万不可不判断强上,另外弹出系统的照片选择器之后

// 打开相册
- (void)openCameralClick:(id)sender {
    // 1.判断相册是否可以打开
    if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) return;
    // 2. 创建图片选择控制器
    UIImagePickerController *ipc = [[UIImagePickerController alloc] init];
    // 3.设置type
    ipc.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    // 4.设置代理
    ipc.delegate = self;
    // 5.modal出这个控制器
    [self presentViewController:ipc animated:YES completion:nil];
}

打开相册之后,需要实现UIImagePickerControllerDelegate,UINavigationControllerDelegate协议,拿到选择回来的照片.也许你好奇为什么这里代码如此之多,因为扫描从相册中取出的二维码跟直接用相机还不一样,需要使用到CIDetector类,这个类貌似人脸识别什么也用,图片探测器.我也遇到了扫描不出的情况,代码中注释github链接也基本解决了我遇到的问题

#pragma mark - UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
    // 注意: 如果实现了该方法, 当选中一张图片时系统就不会自动关闭相册控制器
    [picker dismissViewControllerAnimated:YES completion:nil];
    
    // 1.取出选中的图片
    UIImage *pickImage = (UIImage *)[info objectForKey:UIImagePickerControllerEditedImage];
    
    if (!pickImage){
       pickImage = (UIImage *)[info objectForKey:UIImagePickerControllerOriginalImage];
    }

//    http://stackoverflow.com/questions/10746212/cidetector-and-uiimagepickercontroller
    int exifOrientation;
    switch (pickImage.imageOrientation) {
        case UIImageOrientationUp:
            exifOrientation = 1;
            break;
        case UIImageOrientationDown:
            exifOrientation = 3;
            break;
        case UIImageOrientationLeft:
            exifOrientation = 8;
            break;
        case UIImageOrientationRight:
            exifOrientation = 6;
            break;
        case UIImageOrientationUpMirrored:
            exifOrientation = 2;
            break;
        case UIImageOrientationDownMirrored:
            exifOrientation = 4;
            break;
        case UIImageOrientationLeftMirrored:
            exifOrientation = 5;
            break;
        case UIImageOrientationRightMirrored:
            exifOrientation = 7;
            break;
        default:
            break;
    }
    
    // 2.从选中的图片中读取二维码数据
    // 2.1创建一个探测器
    self.detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}];
    
    // 2.2利用探测器探测数据
    CIImage *theCIImage = [[CIImage alloc] initWithImage:pickImage];
//    CIImage *theCIImage = [CIImage imageWithCGImage:pickImage.CGImage];
    NSArray *features = [self.detector featuresInImage:theCIImage options:@{CIDetectorImageOrientation:[NSNumber numberWithInt:exifOrientation]}];
    
    NSLog(@"theCGImage: %@", pickImage.CGImage);
    NSLog(@"theCIImage: %@", theCIImage);
    NSLog(@"arr: %@", features);
    
    // 2.3取出探测到的数据
    for (CIQRCodeFeature *result in features) {
         NSLog(@"%@",result.messageString);
        NSString *urlStr = result.messageString;
    }
}

最后一个问题是取出用户相册中最新的一张图片展示在扫码界面,这里需要导入#import <Photos/Photos.h>使用到的类是iOS 9之后的PHAsset直接上代码吧.网上有很多老的代码,我找来都是过期了,所以在此记录

    PHFetchOptions *options = [[PHFetchOptions alloc] init];
    PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
    PHAsset *phasset = [assetsFetchResults lastObject];
    PHCachingImageManager *imageManager = [[PHCachingImageManager alloc] init];
    
    [imageManager requestImageForAsset:phasset targetSize:CGSizeMake(300, 300) contentMode:PHImageContentModeAspectFill options:nil resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
        
        // 这里的 result 就是用户最新的image
    }];
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容