iOS相冊、相機、通訊錄權限獲取

一、為什么要獲取權限

在越來越注重個人隱私的今天,用戶很多情況下希望自己能完全掌握自己手機應用對媒體信息的一些訪問權限,比如相冊、相機、通訊錄等。蘋果在iOS7、iOS8等幾個系統版本對一些權限的控制都做了加強,需要用戶授權后應用才有相關的訪問權限。

場景:

  • 在你獲取相冊數據的時候,如果用戶拒絕授權,那么可能會獲取不到數據,此時需要給用戶相應的提示,告知用戶是權限的問題,此時,就需要得知相應的權限狀態給用戶恰當的提示。

  • 用戶的設備沒有相機輸入設備,如果你想訪問用戶的相機,此時就需要判斷用戶設備是否支持,給出恰當的提示。

二、權限狀態說明

  • 相冊、相機、通訊錄等授權狀態目前都有種,都可以對應以下幾種狀態:

    AuthorizationStatusNotDetermined      // 用戶從未進行過授權等處理,首次訪問相應內容會提示用戶進行授權
    AuthorizationStatusAuthorized = 0,    // 用戶已授權,允許訪問
    AuthorizationStatusDenied,            // 用戶拒絕訪問
    AuthorizationStatusRestricted,        // 應用沒有相關權限,且當前用戶無法改變這個權限,比如:家長控制
    

三、權限獲取

  1. 相冊權限
  • 是否支持

     ``` 
     [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]
     ```     
    
  • 獲取權限狀態

     ios8以前
     
     ```
         ALAuthorizationStatus authStatus = [ALAssetsLibrary authorizationStatus];
     ```
     
     ios8及以后
     
     ```
         PHAuthorizationStatus authStatus = [PHPhotoLibrary authorizationStatus];
     ```
    
  • 請求權限

     ```
     [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
                     }];
     ```
    
  • 權限狀態

     iOS8以前
     
     ```
     typedef NS_ENUM(NSInteger, ALAuthorizationStatus) {
         ALAuthorizationStatusNotDetermined NS_ENUM_DEPRECATED_IOS(6_0, 9_0) = 0, // User has not yet made a choice with regards to this application
         ALAuthorizationStatusRestricted NS_ENUM_DEPRECATED_IOS(6_0, 9_0),        // This application is not authorized to access photo data.
                                                 // The user cannot change this application’s status, possibly due to active restrictions
                                                 //  such as parental controls being in place.
         ALAuthorizationStatusDenied NS_ENUM_DEPRECATED_IOS(6_0, 9_0),            // User has explicitly denied this application access to photos data.
         ALAuthorizationStatusAuthorized NS_ENUM_DEPRECATED_IOS(6_0, 9_0)        // User has authorized this application to access photos data.
     } NS_DEPRECATED_IOS(6_0, 9_0, "Use PHAuthorizationStatus in the Photos framework instead");
     ```
     
     iOS8及以后
     
     ```
     typedef NS_ENUM(NSInteger, PHAuthorizationStatus) {
         PHAuthorizationStatusNotDetermined = 0, // User has not yet made a choice with regards to this application
         PHAuthorizationStatusRestricted,        // This application is not authorized to access photo data.
                                                 // The user cannot change this application’s status, possibly due to active restrictions
                                                 //   such as parental controls being in place.
         PHAuthorizationStatusDenied,            // User has explicitly denied this application access to photos data.
         PHAuthorizationStatusAuthorized         // User has authorized this application to access photos data.
     } NS_AVAILABLE_IOS(8_0);
     ```
    
  1. 拍照權限
  • 是否支持

     ```
     [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]
     ``` 
    
  • 獲取權限狀態

     ```
     AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
     ```
    
  • 請求權限

     ```
     [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
                     }];
     ```
    
  • 權限狀態

     ```
     typedef NS_ENUM(NSInteger, AVAuthorizationStatus) {
         AVAuthorizationStatusNotDetermined = 0,
         AVAuthorizationStatusRestricted,
         AVAuthorizationStatusDenied,
         AVAuthorizationStatusAuthorized
     } NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED;
     ```
    
  1. 通訊錄權限
  • 獲取權限狀態

     iOS9以前
     
     ```
     ABAuthorizationStatus authStatus = ABAddressBookGetAuthorizationStatus();
     ```
     
     iOS9及以后
     
     ```
     CNAuthorizationStatus authStatus = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
     ```
    
  • 請求權限

     iOS9以前
     
     ```
     __block ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
     if (addressBook == NULL) {
         [self executeCallback:callback status:WTAuthorizationStatusNotSupport];
         return;
     }
     ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
         if (granted) {
             // 成功
         } else {
             // 失敗
         }
         if (addressBook) {
             CFRelease(addressBook);
             addressBook = NULL;
         }
     });
     ```
     
     iOS9及以后
     
     ```
     CNContactStore *contactStore = [[CNContactStore alloc] init];
     [contactStore requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
         if (granted) {
             // 成功
         } else {
             // 失敗
         }
     }];
     ```
    
  • 權限狀態

     iOS9以前
     
     ```
     typedef CF_ENUM(CFIndex, ABAuthorizationStatus) {
         kABAuthorizationStatusNotDetermined = 0,    // deprecated, use CNAuthorizationStatusNotDetermined
         kABAuthorizationStatusRestricted,           // deprecated, use CNAuthorizationStatusRestricted
         kABAuthorizationStatusDenied,               // deprecated, use CNAuthorizationStatusDenied
         kABAuthorizationStatusAuthorized            // deprecated, use CNAuthorizationStatusAuthorized
     } AB_DEPRECATED("use CNAuthorizationStatus");
     ```
     
     iOS9及以后
     
     ```
     typedef NS_ENUM(NSInteger, CNAuthorizationStatus)
    

{
/*! The user has not yet made a choice regarding whether the application may access contact data. /
CNAuthorizationStatusNotDetermined = 0,
/
! The application is not authorized to access contact data.
* The user cannot change this application’s status, possibly due to active restrictions such as parental controls being in place. /
CNAuthorizationStatusRestricted,
/
! The user explicitly denied access to contact data for the application. /
CNAuthorizationStatusDenied,
/
! The application is authorized to access contact data. */
CNAuthorizationStatusAuthorized
} NS_ENUM_AVAILABLE(10_11, 9_0);
```

四、拒絕授權的處理

  • 用戶拒絕授權后,如果訪問相應內容可能會出現一些類似沒有數據的情況,此時應該給用戶提示,引導用戶授權。

    跳轉到應用設置:

    NSURL *settingUrl = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
    if ([[UIApplication sharedApplication] canOpenURL:settingUrl]) {
        [[UIApplication sharedApplication] openURL:settingUrl];
    }
    

五、簡單封裝示例

  • 工具類

    WTAuthorizationTool.h文件

    #import <Foundation/Foundation.h>
    
    typedef NS_ENUM(NSUInteger, WTAuthorizationStatus) {
        WTAuthorizationStatusAuthorized = 0,    // 已授權
        WTAuthorizationStatusDenied,            // 拒絕
        WTAuthorizationStatusRestricted,        // 應用沒有相關權限,且當前用戶無法改變這個權限,比如:家長控制
        WTAuthorizationStatusNotSupport         // 硬件等不支持
    };
    
    @interface WTAuthorizationTool : NSObject
    
    /**
     *  請求相冊訪問權限
     *
     *  @param callback <#callback description#>
     */
    + (void)requestImagePickerAuthorization:(void(^)(WTAuthorizationStatus status))callback;
    
    /**
     *  請求相機權限
     *
     *  @param callback <#callback description#>
     */
    + (void)requestCameraAuthorization:(void(^)(WTAuthorizationStatus status))callback;
    
    + (void)requestAddressBookAuthorization:(void (^)(WTAuthorizationStatus))callback;
    
    @end
    

    WTAuthorizationTool.m文件

    #import "WTAuthorizationTool.h"
    
    #import <AssetsLibrary/AssetsLibrary.h>
    #import <Photos/Photos.h>
    #import <AddressBook/AddressBook.h>
    #import <AddressBookUI/AddressBookUI.h>
    #import <ContactsUI/ContactsUI.h>
    
    @implementation WTAuthorizationTool
    
    #pragma mark - 相冊
    + (void)requestImagePickerAuthorization:(void(^)(WTAuthorizationStatus status))callback {
        if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera] ||
            [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
            ALAuthorizationStatus authStatus = [ALAssetsLibrary authorizationStatus];
            if (authStatus == ALAuthorizationStatusNotDetermined) { // 未授權
                if ([UIDevice currentDevice].systemVersion.floatValue < 8.0) {
                    [self executeCallback:callback status:WTAuthorizationStatusAuthorized];
                } else {
                    [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
                        if (status == PHAuthorizationStatusAuthorized) {
                            [self executeCallback:callback status:WTAuthorizationStatusAuthorized];
                        } else if (status == PHAuthorizationStatusDenied) {
                            [self executeCallback:callback status:WTAuthorizationStatusDenied];
                        } else if (status == PHAuthorizationStatusRestricted) {
                            [self executeCallback:callback status:WTAuthorizationStatusRestricted];
                        }
                    }];
                }
                
            } else if (authStatus == ALAuthorizationStatusAuthorized) {
                [self executeCallback:callback status:WTAuthorizationStatusAuthorized];
            } else if (authStatus == ALAuthorizationStatusDenied) {
                [self executeCallback:callback status:WTAuthorizationStatusDenied];
            } else if (authStatus == ALAuthorizationStatusRestricted) {
                [self executeCallback:callback status:WTAuthorizationStatusRestricted];
            }
        } else {
            [self executeCallback:callback status:WTAuthorizationStatusNotSupport];
        }
    }
    
    #pragma mark - 相機
    + (void)requestCameraAuthorization:(void (^)(WTAuthorizationStatus))callback {
        if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
            AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
            if (authStatus == AVAuthorizationStatusNotDetermined) {
                [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
                    if (granted) {
                        [self executeCallback:callback status:WTAuthorizationStatusAuthorized];
                    } else {
                        [self executeCallback:callback status:WTAuthorizationStatusDenied];
                    }
                }];
            } else if (authStatus == AVAuthorizationStatusAuthorized) {
                [self executeCallback:callback status:WTAuthorizationStatusAuthorized];
            } else if (authStatus == AVAuthorizationStatusDenied) {
                [self executeCallback:callback status:WTAuthorizationStatusDenied];
            } else if (authStatus == AVAuthorizationStatusRestricted) {
                [self executeCallback:callback status:WTAuthorizationStatusRestricted];
            }
        } else {
            [self executeCallback:callback status:WTAuthorizationStatusNotSupport];
        }
    }
    
    #pragma mark - 通訊錄
    + (void)requestAddressBookAuthorization:(void (^)(WTAuthorizationStatus))callback {
        ABAuthorizationStatus authStatus = ABAddressBookGetAuthorizationStatus();
        if (authStatus == kABAuthorizationStatusNotDetermined) {
            __block ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
            if (addressBook == NULL) {
                [self executeCallback:callback status:WTAuthorizationStatusNotSupport];
                return;
            }
            ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
                if (granted) {
                    [self executeCallback:callback status:WTAuthorizationStatusAuthorized];
                } else {
                    [self executeCallback:callback status:WTAuthorizationStatusDenied];
                }
                if (addressBook) {
                    CFRelease(addressBook);
                    addressBook = NULL;
                }
            });
            return;
        } else if (authStatus == kABAuthorizationStatusAuthorized) {
            [self executeCallback:callback status:WTAuthorizationStatusAuthorized];
        } else if (authStatus == kABAuthorizationStatusDenied) {
            [self executeCallback:callback status:WTAuthorizationStatusDenied];
        } else if (authStatus == kABAuthorizationStatusRestricted) {
            [self executeCallback:callback status:WTAuthorizationStatusRestricted];
        }
    }
    
    #pragma mark - callback
    + (void)executeCallback:(void (^)(WTAuthorizationStatus))callback status:(WTAuthorizationStatus)status {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (callback) {
                    callback(status);
            }
        });
    }
    
    @end
    
  • 界面測試提示

    controller部分代碼

    - (void)requestAddressBook {
        [WTAuthorizationTool requestAddressBookAuthorization:^(WTAuthorizationStatus status) {
            [self requestAuthCallback:status];
        }];
    }
    
    - (void)requestCamera {
        [WTAuthorizationTool requestCameraAuthorization:^(WTAuthorizationStatus status) {
            [self requestAuthCallback:status];
        }];
    }
    
    - (void)requestAlbum {
        [WTAuthorizationTool requestImagePickerAuthorization:^(WTAuthorizationStatus status) {
            [self requestAuthCallback:status];
        }];
    }
    
    - (void)requestAuthCallback:(WTAuthorizationStatus)status {
        switch (status) {
            case WTAuthorizationStatusAuthorized:
                [WTAlert showAlertFrom:self title:@"授權成功" message:@"可以訪問你要訪問的內容了" cancelButtonTitle:@"我知道了" cancle:^{
                    
                } confirmButtonTitle:nil confirm:nil];
                break;
                
            case WTAuthorizationStatusDenied:
            case WTAuthorizationStatusRestricted:
                [WTAlert showAlertFrom:self title:@"授權失敗" message:@"用戶拒絕" cancelButtonTitle:@"我知道了" cancle:^{
                    
                } confirmButtonTitle:@"現在設置" confirm:^{
                    NSURL *settingUrl = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
                    if ([[UIApplication sharedApplication] canOpenURL:settingUrl]) {
                        [[UIApplication sharedApplication] openURL:settingUrl];
                    }
                }];
                break;
                
            case WTAuthorizationStatusNotSupport:
                [WTAlert showAlertFrom:self title:@"授權失敗" message:@"設備不支持" cancelButtonTitle:@"我知道了" cancle:^{
                    
                } confirmButtonTitle:nil confirm:nil];
                
            default:
                break;
        }
    }
    

六、源碼

完整源碼

  • 使用WTAuthorizationTool

    pod "WTAuthorizationTool"
    
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,743評論 2 370

推薦閱讀更多精彩內容