确定是否设置了对照片库的访问权限-PHPhotoLibrary

有了 iOS 8的新功能,如果你在应用程序中使用相机,它会请求允许访问相机,然后当你尝试重拍照片时,它会请求允许访问照片库。下次当我启动应用程序时,我希望检查相机和照片库是否有访问权限。

enter image description here

为了照相,我检查了一下

if ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] == AVAuthorizationStatusDenied)
{
// do something
}

我正在寻找类似的东西为图片库。

112424 次浏览

Check +[PHPhotoLibrary authorizationStatus] – if not set, it will return PHAuthorizationStatusNotDetermined. (You can then request access using +requestAuthorization: on the same class.)

Using ALAssetsLibrary should work:

ALAuthorizationStatus status = [ALAssetsLibrary authorizationStatus];
switch (status) {
case ALAuthorizationStatusNotDetermined: {
// not determined
break;
}
case ALAuthorizationStatusRestricted: {
// restricted
break;
}
case ALAuthorizationStatusDenied: {
// denied
break;
}
case ALAuthorizationStatusAuthorized: {
// authorized
break;
}
default: {
break;
}
}

Here is a small and simple snippet that I usually use.

- (void)requestPhotoAuthorization:(void (^)(BOOL granted))granted
{
void (^handler)(PHAuthorizationStatus) = ^(PHAuthorizationStatus status)
{
if (status == PHAuthorizationStatusAuthorized) granted(YES);
else if (status == PHAuthorizationStatusNotDetermined) [PHPhotoLibrary requestAuthorization:handler];
else granted(NO);
};
handler([PHPhotoLibrary authorizationStatus]);
}

I did it like this:

- (void)requestPermissions:(GalleryPermissions)block
{
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];


switch (status)
{
case PHAuthorizationStatusAuthorized:
block(YES);
break;
case PHAuthorizationStatusNotDetermined:
{
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus authorizationStatus)
{
if (authorizationStatus == PHAuthorizationStatusAuthorized)
{
block(YES);
}
else
{
block(NO);
}
}];
break;
}
default:
block(NO);
break;
}
}

And i send in what I need to do as the block depending on success or failure.

I know this has already been answered, but just to expand on @Tim answer, here is the code you need (iOS 8 and above):

PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];


if (status == PHAuthorizationStatusAuthorized) {
// Access has been granted.
}


else if (status == PHAuthorizationStatusDenied) {
// Access has been denied.
}


else if (status == PHAuthorizationStatusNotDetermined) {


// Access has not been determined.
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {


if (status == PHAuthorizationStatusAuthorized) {
// Access has been granted.
}


else {
// Access has been denied.
}
}];
}


else if (status == PHAuthorizationStatusRestricted) {
// Restricted access - normally won't happen.
}

Don't forget to #import <Photos/Photos.h>

If you are using Swift 3.0 or higher, you can use the following code:

// Get the current authorization state.
let status = PHPhotoLibrary.authorizationStatus()


if (status == PHAuthorizationStatus.authorized) {
// Access has been granted.
}


else if (status == PHAuthorizationStatus.denied) {
// Access has been denied.
}


else if (status == PHAuthorizationStatus.notDetermined) {


// Access has not been determined.
PHPhotoLibrary.requestAuthorization({ (newStatus) in


if (newStatus == PHAuthorizationStatus.authorized) {


}


else {


}
})
}


else if (status == PHAuthorizationStatus.restricted) {
// Restricted access - normally won't happen.
}

Don't forget to import Photos

I have a simple solution on swift 2.0


//
//  AppDelegate.swift
//  HoneyBadger
//
//  Created by fingent on 14/08/15.
//  Copyright (c) 2015 fingent. All rights reserved.
//


import UIKit
import Photos


@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {


var window: UIWindow?


func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
self.window?.makeKeyAndVisible()


self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewControllerWithIdentifier("LoginPageID")
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
return true
}
func applicationDidEnterBackground(application: UIApplication) {
print("Application On background", terminator: "")
}
func applicationDidBecomeActive(application: UIApplication) {
cameraAllowsAccessToApplicationCheck()
photoLibraryAvailabilityCheck()
}
//MARK:- CAMERA ACCESS CHECK
func cameraAllowsAccessToApplicationCheck()
{
let authorizationStatus = AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo)
switch authorizationStatus {
case .NotDetermined:
// permission dialog not yet presented, request authorization
AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo,
completionHandler: { (granted:Bool) -> Void in
if granted {
print("access granted", terminator: "")
}
else {
print("access denied", terminator: "")
}
})
case .Authorized:
print("Access authorized", terminator: "")
case .Denied, .Restricted:
alertToEncourageCameraAccessWhenApplicationStarts()
default:
print("DO NOTHING", terminator: "")
}
}
//MARK:- PHOTO LIBRARY ACCESS CHECK
func photoLibraryAvailabilityCheck()
{
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.Authorized
{


}
else
{
PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler)
}
}
func requestAuthorizationHandler(status: PHAuthorizationStatus)
{
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.Authorized
{


}
else
{
alertToEncouragePhotoLibraryAccessWhenApplicationStarts()
}
}


//MARK:- CAMERA & GALLERY NOT ALLOWING ACCESS - ALERT
func alertToEncourageCameraAccessWhenApplicationStarts()
{
//Camera not available - Alert
let internetUnavailableAlertController = UIAlertController (title: "Camera Unavailable", message: "Please check to see if it is disconnected or in use by another application", preferredStyle: .Alert)


let settingsAction = UIAlertAction(title: "Settings", style: .Destructive) { (_) -> Void in
let settingsUrl = NSURL(string:UIApplicationOpenSettingsURLString)
if let url = settingsUrl {
dispatch_async(dispatch_get_main_queue()) {
UIApplication.sharedApplication().openURL(url)
}


}
}
let cancelAction = UIAlertAction(title: "Okay", style: .Default, handler: nil)
internetUnavailableAlertController .addAction(settingsAction)
internetUnavailableAlertController .addAction(cancelAction)
self.window?.rootViewController!.presentViewController(internetUnavailableAlertController , animated: true, completion: nil)
}
func alertToEncouragePhotoLibraryAccessWhenApplicationStarts()
{
//Photo Library not available - Alert
let cameraUnavailableAlertController = UIAlertController (title: "Photo Library Unavailable", message: "Please check to see if device settings doesn't allow photo library access", preferredStyle: .Alert)


let settingsAction = UIAlertAction(title: "Settings", style: .Destructive) { (_) -> Void in
let settingsUrl = NSURL(string:UIApplicationOpenSettingsURLString)
if let url = settingsUrl {
UIApplication.sharedApplication().openURL(url)
}
}
let cancelAction = UIAlertAction(title: "Okay", style: .Default, handler: nil)
cameraUnavailableAlertController .addAction(settingsAction)
cameraUnavailableAlertController .addAction(cancelAction)
self.window?.rootViewController!.presentViewController(cameraUnavailableAlertController , animated: true, completion: nil)
}
}

Just as formality, Swift 2.X version:

    func checkPhotoLibraryPermission() {
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .Authorized:
//handle authorized status
case .Denied, .Restricted :
//handle denied status
case .NotDetermined:
// ask for permissions
PHPhotoLibrary.requestAuthorization() { (status) -> Void in
switch status {
case .Authorized:
// as above
case .Denied, .Restricted:
// as above
case .NotDetermined:
// won't happen but still
}
}
}
}

And Swift 3 / Swift 4:

    import Photos


func checkPhotoLibraryPermission() {
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .authorized:
//handle authorized status
case .denied, .restricted :
//handle denied status
case .notDetermined:
// ask for permissions
PHPhotoLibrary.requestAuthorization { status in
switch status {
case .authorized:
// as above
case .denied, .restricted:
// as above
case .notDetermined:
// won't happen but still
}
}
}
}

Swift 2.0+

Based on a combination of answers here, I've created a solution for myself. This method only checks if there is no permission.

We got a method pickVideo() that requires access to photos. If it is not .Authorized ask for permission.

If permission is not given, pickVideo() will not be called, and the user cannot pick a video.

As long as the user did not give full access to photos, you can avoid to let them pick 'or crash' your application.

  // Method that requires access to photos
func pickVideo(){
// Check for permission
if PHPhotoLibrary.authorizationStatus() != .Authorized{
// If there is no permission for photos, ask for it
PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler)
return
}
//... pick video code here...
}


func requestAuthorizationHandler(status: PHAuthorizationStatus){
if PHPhotoLibrary.authorizationStatus() == .Authorized{
// The user did authorize, so, pickVideo may be opened
// Ensure pickVideo is called from the main thread to avoid GUI problems
dispatch_async(dispatch_get_main_queue()) {
pickVideo()
}
} else {
// Show Message to give permission in Settings
let alertController = UIAlertController(title: "Error", message: "Enable photo permissions in settings", preferredStyle: .Alert)
let settingsAction = UIAlertAction(title: "Settings", style: .Default) { (alertAction) in
if let appSettings = NSURL(string: UIApplicationOpenSettingsURLString) {
UIApplication.sharedApplication().openURL(appSettings)
}
}
alertController.addAction(settingsAction)
// If user cancels, do nothing, next time Pick Video is called, they will be asked again to give permission
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
alertController.addAction(cancelAction)
// Run GUI stuff on main thread
dispatch_async(dispatch_get_main_queue()) {
self.presentViewController(alertController, animated: true, completion: nil)
}
}
}

Here is a complete guide for iOS 8+ (without ALAssetLibrary):

First, we have to provide the usage description as now it's required by PHPhotoLibrary.
For doing this we must open the file info.plist, find the key Privacy - Photo Library Usage Description and provide the value for it. If the key doesn't exist then just create it.
Here is an image for example:
enter image description here Also make sure that the value of the key Bundle name is not empty in the info.plist file.

Now when we have description, we can normally request authorization by calling requestAuthorization method:

[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
switch (status) {
case PHAuthorizationStatusAuthorized:
NSLog(@"PHAuthorizationStatusAuthorized");
break;
case PHAuthorizationStatusDenied:
NSLog(@"PHAuthorizationStatusDenied");
break;
case PHAuthorizationStatusNotDetermined:
NSLog(@"PHAuthorizationStatusNotDetermined");
break;
case PHAuthorizationStatusRestricted:
NSLog(@"PHAuthorizationStatusRestricted");
break;
}
}];

NOTE 1: requestAuthorization actually doesn't show alert on every call. It shows once per some time, saves user's answer and returns it everytime instead of showing alert again. But as it isn't what we need, here is a useful code which always shows alert every time we need permission (with redirection to settings):

- (void)requestAuthorizationWithRedirectionToSettings {
dispatch_async(dispatch_get_main_queue(), ^{
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
if (status == PHAuthorizationStatusAuthorized)
{
//We have permission. Do whatever is needed
}
else
{
//No permission. Trying to normally request it
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if (status != PHAuthorizationStatusAuthorized)
{
//User don't give us permission. Showing alert with redirection to settings
//Getting description string from info.plist file
NSString *accessDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSPhotoLibraryUsageDescription"];
UIAlertController * alertController = [UIAlertController alertControllerWithTitle:accessDescription message:@"To give permissions tap on 'Change Settings' button" preferredStyle:UIAlertControllerStyleAlert];
                    

UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil];
[alertController addAction:cancelAction];
                    

UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:@"Change Settings" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
}];
[alertController addAction:settingsAction];
                    

[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}
}];
}
});
}

Common problem 1: Some users complain that the app doesn't show an alert after doing the above-mentioned changes in the info.plist file.
Solution: For testing try to change Bundle Identifier from the project file to something else, clean and rebuild the app. If it started working then everything is fine, rename it back.

Common Problem 2: There is some specific case when fetch results are not updated (and the views which used images from those fetch requests still empty accordingly) when the app gets permissions to photos, while running as it was promised in the documentation.
Actually it happens when we use WRONG code like this:

- (void)viewDidLoad {
if ([PHPhotoLibrary authorizationStatus] != PHAuthorizationStatusAuthorized)
{
//Reloading some view which needs photos
[self reloadCollectionView];
// ...
} else {
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if (status == PHAuthorizationStatusAuthorized)
[self reloadCollectionView];
// ...
}];
}
// ...
}

In this case, if the user denied giving permissions on viewDidLoad then jumped to settings, allowed and jumped back to the the app, views will not be refreshed because [self reloadCollectionView] and fetch requests were not sent.
Solution: We just have to call [self reloadCollectionView] and do other fetch requests before requiring authorization like this:

- (void)viewDidLoad {
//Reloading some view which needs photos
[self reloadCollectionView];
if ([PHPhotoLibrary authorizationStatus] != PHAuthorizationStatusAuthorized)
{
// ...
}

UPDATE for: SWIFT 3 IOS10


Note: import Photos in AppDelegate.swift as follows

// AppDelegate.swift

import UIKit

import Photos

...


func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
photoLibraryAvailabilityCheck()


}


//MARK:- PHOTO LIBRARY ACCESS CHECK
func photoLibraryAvailabilityCheck()
{
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized
{


}
else
{
PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler)
}
}
func requestAuthorizationHandler(status: PHAuthorizationStatus)
{
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized
{


}
else
{
alertToEncouragePhotoLibraryAccessWhenApplicationStarts()
}
}


//MARK:- CAMERA & GALLERY NOT ALLOWING ACCESS - ALERT
func alertToEncourageCameraAccessWhenApplicationStarts()
{
//Camera not available - Alert
let internetUnavailableAlertController = UIAlertController (title: "Camera Unavailable", message: "Please check to see if it is disconnected or in use by another application", preferredStyle: .alert)


let settingsAction = UIAlertAction(title: "Settings", style: .destructive) { (_) -> Void in
let settingsUrl = NSURL(string:UIApplicationOpenSettingsURLString)
if let url = settingsUrl {
DispatchQueue.main.async {
UIApplication.shared.open(url as URL, options: [:], completionHandler: nil) //(url as URL)
}


}
}
let cancelAction = UIAlertAction(title: "Okay", style: .default, handler: nil)
internetUnavailableAlertController .addAction(settingsAction)
internetUnavailableAlertController .addAction(cancelAction)
self.window?.rootViewController!.present(internetUnavailableAlertController , animated: true, completion: nil)
}
func alertToEncouragePhotoLibraryAccessWhenApplicationStarts()
{
//Photo Library not available - Alert
let cameraUnavailableAlertController = UIAlertController (title: "Photo Library Unavailable", message: "Please check to see if device settings doesn't allow photo library access", preferredStyle: .alert)


let settingsAction = UIAlertAction(title: "Settings", style: .destructive) { (_) -> Void in
let settingsUrl = NSURL(string:UIApplicationOpenSettingsURLString)
if let url = settingsUrl {
UIApplication.shared.open(url as URL, options: [:], completionHandler: nil)
}
}
let cancelAction = UIAlertAction(title: "Okay", style: .default, handler: nil)
cameraUnavailableAlertController .addAction(settingsAction)
cameraUnavailableAlertController .addAction(cancelAction)
self.window?.rootViewController!.present(cameraUnavailableAlertController , animated: true, completion: nil)
}

Answer updated from Alvin George

iOS 14 onwards Apple has added a new feature that will give limited access to the photos library. Based on your requirements (Example creating custom photo gallery) you have to check if user has given limited access only and wants to give full access.

For backwards compatibility, the old versions without the parameter return .authorized even when you get limited access.

Swift 5:

switch PHPhotoLibrary.authorizationStatus(for: .readWrite) {
case .notDetermined:
// ask for access
case .restricted, .denied:
// sorry
case .authorized:
// we have full access
 

// new option:
case .limited:
// we only got access to some photos of library
}

There is a code to call limited access screen again. if user has given .limited access only and you want user to select the images again.

PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: presentVCObj)

On every restart of app iOS will show alert to notify user about the limited access. If you want to stop that alert then add PHPhotoLibraryPreventAutomaticLimitedAccessAlert to YES in Info.plist