Determine si el acceso a la biblioteca de fotos está configurado o no - PHPhotoLibrary


101

Con la nueva funcionalidad en iOS 8, si está usando una cámara en la aplicación, le pedirá permiso para acceder a la cámara y luego, cuando intente volver a tomar la foto, le pedirá permiso para acceder a la biblioteca de fotos. La próxima vez que abra la aplicación, deseo comprobar si la cámara y la biblioteca de fotos tienen permisos de acceso.

ingrese la descripción de la imagen aquí

Para la cámara, lo reviso por

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

Estoy buscando algo similar a esto para la biblioteca de fotos.

Respuestas:


85

Verificar +[PHPhotoLibrary authorizationStatus]- si no está configurado, regresará PHAuthorizationStatusNotDetermined. (A continuación, puede solicitar acceso utilizando +requestAuthorization:en la misma clase).


¿Necesito agregar / importar alguna base o biblioteca para usar PHPhotoLibrary? Recibo un error "Uso de identificador no declarado"
tech_human

2
Intenté usar "ALAssetsLibrary" en su lugar para verificar el estado de la autorización y eso devuelve SÍ incluso si la biblioteca de fotos está apagada.
tech_human

Oh, puedo obtener el estado usando "ALAssetsLibrary". Todavía tengo curiosidad por saber si es posible usar la biblioteca de fotos PHP.
tech_human

3
PHPhotoLibrary es parte del marco de fotos, que solo está disponible en iOS 8. Si necesita soporte para versiones anteriores de iOS, ALAssetsLibrary es probablemente su mejor opción.
Tim

Bueno, a partir de iOS 9, ALAssetsLibrary está en desuso, así que supongo que es por eso que no funciona.
Supertecnoboff

131

Sé que esto ya ha sido respondido, pero solo para expandir la respuesta de @Tim, aquí está el código que necesita (iOS 8 y superior):

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.
}

No te olvides de #import <Photos/Photos.h>

Si está utilizando Swift 3.0 o superior, puede utilizar el siguiente código:

// 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.
}

No te olvides de import Photos


5
¿Por qué es esto solo iOS 9 y superior? El marco de fotos está disponible desde iOS 8 ..
Balázs Vincze

1
Además, no olvide agregar el marco de fotos en el Proyecto -> Destino -> Fases de compilación
stellz

No funciona correctamente, denegué el acceso y luego lo habilité de nuevo, todavía dice no determinado.
TomSawyer

"// Acceso restringido - normalmente no sucederá". ¿Por qué? Podría suceder: "El usuario no puede cambiar el estado de esta aplicación, posiblemente debido a restricciones activas"
NoKey

¿PHPhotoLibrary.requestAuthorization supone mostrar un diálogo pidiendo permiso? Porque llamar a esta línea no hace nada
iori24

49

Como formalidad, la versión Swift 2.X :

    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
               }
            }
        }
    }

Y 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
                }
            }
        }
    }

6
En Swift 3 , no se olvide import Photos, si desea utilizar PHPhotoLibrary
ronatory

27

Aquí hay una guía completa para iOS 8+ (sin ALAssetLibrary):

En primer lugar, debemos proporcionar una descripción de uso , ya que ahora es requerida por PHPhotoLibrary.
Para hacer esto debemos abrir el info.plistarchivo, buscar la clave Privacy - Photo Library Usage Descriptiony darle un valor. Si la clave no existe, créala.
Aquí hay una imagen por ejemplo:
ingrese la descripción de la imagen aquí También asegúrese de que el valor de la clave Bundle nameno esté vacío eninfo.plist archivo.

Ahora que tenemos una descripción, normalmente podemos solicitar autorización llamando al requestAuthorizationmétodo:

[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;
    }
}];

NOTA 1: en requestAuthorization realidad, no muestra alerta en cada llamada. Se muestra una vez cada cierto tiempo, guarda la respuesta del usuario y la devuelve cada vez en lugar de mostrar la alerta nuevamente. Pero como no es lo que necesitamos, aquí hay un código útil que siempre muestra una alerta cada vez que necesitamos permiso (con redirección a la configuración):

- (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];
                }
            }];
        }
    });
}

Problema común 1: algunos usuarios se quejan de que la aplicación no muestra una alerta después de realizar los cambios mencionados anteriormente en el info.plistarchivo.
Solución: Para probar, intente cambiar Bundle Identifierdel archivo del proyecto a otra cosa, limpie y reconstruya la aplicación. Si comenzó a funcionar, entonces todo está bien, cámbiele el nombre.

Problema común 2: hay un caso específico en el que los resultados de recuperación no se actualizan (y las vistas que usaron imágenes de esas solicitudes de recuperación siguen vacías en consecuencia) cuando la aplicación obtiene permisos para las fotos, mientras se ejecuta como se prometió en la documentación.
En realidad sucede cuando usamos código INCORRECTO como este:

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

En este caso, si el usuario negó otorgar permisos y viewDidLoadluego saltó a la configuración, permitió y saltó de nuevo a la aplicación, las vistas no se actualizarán porque [self reloadCollectionView]no se enviaron las solicitudes de recuperación.
Solución: solo tenemos que llamar [self reloadCollectionView]y realizar otras solicitudes de recuperación antes de requerir una autorización como esta:

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

¿Cómo se vincula la configuración de la aplicación a los permisos?
user2924482

20

Lo hice así:

- (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;
    }
}

Y envío lo que tengo que hacer como bloqueo dependiendo del éxito o el fracaso.


8

ACTUALIZACIÓN para: SWIFT 3 IOS10


Nota: importe fotos en AppDelegate.swift de la siguiente manera

// AppDelegate.swift

importar UIKit

importar fotos

...


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)
}

Respuesta actualizada de Alvin George


5

El uso de ALAssetsLibrary debería funcionar:

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;
    }
}

3
Excelente respuesta, pero está obsoleta en iOS 9.
Supertecnoboff

3
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)
    }
}

0

Aquí hay un pequeño y simple fragmento que suelo usar.

- (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]);
}

2
¿No parece volver concedido (SÍ) o concedido (NO) si no está determinado?
Sombras

como arriba + capturar 'manejador' fuertemente en este bloque es probable que conduzca a un ciclo de retención
Ernest

0

Swift 2.0+

Basado en una combinación de respuestas aquí, he creado una solución para mí. Este método solo verifica si no hay permiso.

Tenemos un método pickVideo()que requiere acceso a fotos. Si no es.Authorized pida permiso.

Si no se da permiso, pickVideo() no se le llamará y el usuario no podrá elegir un video.

Siempre que el usuario no le dé acceso completo a las fotos, puede evitar dejar que escoja "o bloquee" su aplicación.

  // 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)
        }
      }
    }
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.