在實際項目中,我們經常需要訪問設備的攝像頭或者相冊,當第一次安裝某個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)當只是要開關某個權限的時候,進入設置-->隱私 裡面開關即可。