iOS开发实战——摄像头与相册权限获取逻辑优化

Posted 乞力马扎罗的雪CYF

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS开发实战——摄像头与相册权限获取逻辑优化相关的知识,希望对你有一定的参考价值。

       在实际项目中,我们经常需要访问设备的摄像头或者相册,当第一次安装某个App的时候,系统便会弹出授权对话框,要求用户做出是否授权的判断。整体逻辑比较简单,但是在使用过程中需要对用户体验进行优化,否则会出现bug。该博客的示例代码已经上传至 https://github.com/chenyufeng1991/AuthorityOfCameraAndPhoto 。

       首先我先描述一下出现的问题。我以访问相册为例,实现代码如下:

- (void)photoBtnPressed:(id)sender
{
    // 首先查看当前设备是否支持相册
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary])
    {
        [self presentToImagePickerController:UIImagePickerControllerSourceTypePhotoLibrary];
    }
    else
    {
        [self showAlertController:@"提示" message:@"当前设备不支持相册"];
    }
}


- (void)presentToImagePickerController:(UIImagePickerControllerSourceType)type
{
    UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    picker.delegate = self;
    picker.allowsEditing = YES;
    picker.sourceType = type;
    [self presentViewController:picker animated:YES completion:nil];
}

- (void)showAlertController:(NSString *)title message:(NSString *)message
{
    UIAlertController *ac = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
    [ac addAction:[UIAlertAction actionWithTitle:@"我知道了" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

    }]];
    [self presentViewController:ac animated:YES completion:nil];
}


解释一下,首先需要判断当前设备是否支持相册,进行这样判断操作会比较安全。如果可以的话直接使用UIImagePickerController访问,否则弹出alert提示框。第一次运行效果如下:


该对话框就是系统请求用户获得访问相册权限的对话框,如果点击“OK”,那么就能弹出相册界面。如果点击"Don't Allow",用户就无法访问相册,因为我这里要演示交互问题,所以我点击"Don't Allow".此时出现如下的空白界面:


这样就会出现交互问题,跳到了一个完全空白的页面,并且没有任何的提示,准确来说,这就是一个bug。而且我们无法对这个空白页面进行自定义。如果大家仔细观察这个权限获得的过程,发现界面是首先弹出这个空白页面,然后才是弹出选择对话框。这就是问题所在,获取摄像头权限也是一样的,下面我们就来解决这类问题。

       我的目标是首先弹出授权对话框,如果我允许授权,那么就跳到摄像头界面或者相册界面;如果我拒绝授权,那么就跳到一个带有提示的自定义页面。首先以相册为例来实现:

(1)首先说明下授权状态,共有三种:

已授权:***Authorized;

未确定:***NotDetrmined;

已拒绝:***Denied,***Restricted;

对于当前设备的这些权限状态,我们可以直接读取,我实现了以下方法:

+ (BOOL)isPhotoAlbumDenied
{
    ALAuthorizationStatus author = [ALAssetsLibrary authorizationStatus];
    if (author == ALAuthorizationStatusRestricted || author == ALAuthorizationStatusDenied)
    {
        return YES;
    }
    return NO;
}

+ (BOOL)isPhotoAlbumNotDetermined
{
    ALAuthorizationStatus author = [ALAssetsLibrary authorizationStatus];
    if (author == ALAuthorizationStatusNotDetermined)
    {
        return YES;
    }
    return NO;
}

isPhotoAlbumDenied方法判断相册权限是否已经被拒绝;isPhotoAlbumNotDetermined方法判断是否还没确定。方法接口写在YFKit类中。


(2)授权方法实现如下:

- (void)optimalPhotoBtnPressed:(id)sender
{
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary])
    {
        // 第一次安装App,还未确定权限,调用这里
        if ([YFKit isPhotoAlbumNotDetermined])
        {
            if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
            {
                // 该API从ios8.0开始支持
                // 系统弹出授权对话框
                [phphotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        if (status == PHAuthorizationStatusRestricted || status == PHAuthorizationStatusDenied)
                        {
                            // 用户拒绝,跳转到自定义提示页面
                            DeniedAuthViewController *vc = [[DeniedAuthViewController alloc] init];
                            [self presentViewController:vc animated:YES completion:nil];
                        }
                        else if (status == PHAuthorizationStatusAuthorized)
                        {
                            // 用户授权,弹出相册对话框
                            [self presentToImagePickerController:UIImagePickerControllerSourceTypePhotoLibrary];
                        }
                    });
                }];
            }
            else
            {
                // 以上requestAuthorization接口只支持8.0以上,如果App支持7.0及以下,就只能调用这里。
                [self presentToImagePickerController:UIImagePickerControllerSourceTypePhotoLibrary];
            }
        }
        else if ([YFKit isPhotoAlbumDenied])
        {
            // 如果已经拒绝,则弹出对话框
            [self showAlertController:@"提示" message:@"拒绝访问相册,可去设置隐私里开启"];
        }
        else
        {
            // 已经授权,跳转到相册页面
            [self presentToImagePickerController:UIImagePickerControllerSourceTypePhotoLibrary];
        }
    }
    else
    {
        // 当前设备不支持打开相册
        [self showAlertController:@"提示" message:@"当前设备不支持相册"];
    }
}


(3)运行效果如下:

申请授权:


可以看到此时是先弹出对话框进行确认的,而不是跳到相册空白页面才进行弹出确认的。


拒绝授权:


该空态界面可以自定义。


允许授权:


直接跳到相册页面了。


(3)摄像头申请授权逻辑与相册类似,只是使用的API不同,但是更为简单,因为该API可以支持7.0及以上,而目前的App都基本支持7.0及以上。使用的接口是AVCaptureDevice。实现方法如下:

- (void)optimalCameraBtnPressed:(id)sender
{
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
    {
        // 应用第一次申请权限调用这里
        if ([YFKit isCameraNotDetermined])
        {
            [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    if (granted)
                    {
                        // 用户授权
                        [self presentToImagePickerController:UIImagePickerControllerSourceTypeCamera];
                    }
                    else
                    {
                        // 用户拒绝授权
                        DeniedAuthViewController *vc = [[DeniedAuthViewController alloc] init];
                        [self presentViewController:vc animated:YES completion:nil];
                    }
                });
            }];
        }
        // 用户已经拒绝访问摄像头
        else if ([YFKit isCameraDenied])
        {
            [self showAlertController:@"提示" message:@"拒绝访问摄像头,可去设置隐私里开启"];
        }

        // 用户允许访问摄像头
        else
        {
            [self presentToImagePickerController:UIImagePickerControllerSourceTypeCamera];
        }
    }
    else
    {
        // 当前设备不支持摄像头,比如模拟器
        [self showAlertController:@"提示" message:@"当前设备不支持拍照"];
    }
}
测试摄像头需要在真机下进行测试,因为模拟器不支持摄像头。

       通过以上代码,可以有效并且可控的进行摄像头和相册权限申请的流程控制,优化用户体验。下面给出一些开发tips:

(1)对于模拟器,如果想要重置应用的权限与隐私设置,可以直接重置模拟器,选择Simulator-->Reset Content and Setting即可。下次重新安装App时,所有的权限都要重新申请了。

(2)在真机上重置权限可以进入:设置-->通用-->重置-->重置位置与隐私即可。这种重置方式是安全的,不会导致手机上的其他数据的丢失,仅仅只是把某些权限记录给删除了。当需要使用权限的时候,系统会重新申请。

(3)当只是要开关某个权限的时候,进入设置-->隐私 里面开关即可。


以上是关于iOS开发实战——摄像头与相册权限获取逻辑优化的主要内容,如果未能解决你的问题,请参考以下文章

iOS9中,swift判断相机,相册权限,选取图片为头像

iOS开发之调用手机摄像头和相册

NR - iOS / Android 相机/相册/日历/定位 等权限 检测+申请代码

NR - iOS / Android 相机/相册/日历/定位 等权限 检测+申请代码

NR - iOS / Android 相机/相册/日历/定位 等权限 检测+申请代码

iOS 使用AFN 进行单图和多图上传 摄像头/相册获取图片,压缩图片