在实际项目中,我们经常需要访问设备的摄像头或者相册,当第一次安装某个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]; }
。
该对话框就是系统请求用户获得访问相册权限的对话框,如果点击“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; }
(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)当只是要开关某个权限的时候,进入设置-->隐私 里面开关即可。
原文:http://blog.csdn.net/chenyufeng1991/article/details/51731217