Detectar permiso de cámara en iOS

143

Estoy desarrollando una aplicación de video muy simple. Yo uso el control oficial: UIImagePickerController.

Aquí está el problema. Al presentar el UIImagePickerController por primera vez, el iOS solicitará el permiso. El usuario puede hacer clic en sí o no. Si el usuario hace clic en no, el control no se descarta. En cambio, si el usuario sigue haciendo clic en el botón de inicio, los temporizadores continúan mientras la pantalla siempre está en negro, y el usuario no puede detener los temporizadores o volver. Lo único que puede hacer el usuario es matar la aplicación. La próxima vez que se presente el UIImagePickerController, sigue siendo una pantalla en negro y el usuario no puede regresar si hace clic en Inicio.

Me preguntaba si es un error. ¿Hay alguna forma de que podamos detectar el permiso de la cámara para que podamos decidir mostrar el UIImagePickerController o no?

usuario418751
fuente
Re: ¿es un error? En mi humilde opinión, creo que sí, porque lo que parece suceder es que el VC está mostrando los datos del hardware, pero el sistema operativo está básicamente enviando aire muerto. La forma en que iOS llegó aquí es probablemente un efecto secundario de la evolución de la familia de productos. UIImageViewControllerse nota como agregado en iOS 2.0, y los documentos nunca se anotaron para reflejar que el AVAuthorizationStatus debería usarse, pero vive en otro marco.
benc

Respuestas:

229

Verifique AVAuthorizationStatusy maneje las cajas correctamente.

NSString *mediaType = AVMediaTypeVideo;
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:mediaType];
if(authStatus == AVAuthorizationStatusAuthorized) {
  // do your logic
} else if(authStatus == AVAuthorizationStatusDenied){
  // denied
} else if(authStatus == AVAuthorizationStatusRestricted){
  // restricted, normally won't happen
} else if(authStatus == AVAuthorizationStatusNotDetermined){
  // not determined?!
  [AVCaptureDevice requestAccessForMediaType:mediaType completionHandler:^(BOOL granted) {
    if(granted){
      NSLog(@"Granted access to %@", mediaType);
    } else {
      NSLog(@"Not granted access to %@", mediaType);
    }
  }];
} else {
  // impossible, unknown authorization status
}
Rapaz
fuente
19
también requiere: #import <AVFoundation/AVFoundation.h>o similar
toblerpwn 01 de
9
Un consejo posiblemente útil: si está probando un código que usa esto, no puede simplemente eliminar su aplicación del dispositivo de prueba y luego volver a instalarla. ¡Hacer esto no hará que iOS vuelva a emitir la solicitud al usuario! Sin embargo, lo que funcionó para mí es cambiar Bundle IDla aplicación cada vez que quiero probar esto. Un dolor en el trasero, pero algo, al menos. Solo recuerde volver a configurar la ID cuando haya terminado ;-)
Benjohn
25
@Benjohn: no es necesario cambiar la ID del paquete. Puede ir a Configuración> General> Restablecer y buscar una configuración que restablezca todas las solicitudes de permiso en el dispositivo. De acuerdo, eso también es molesto, ya que afecta a todas las otras aplicaciones en su dispositivo también. Si Apple pudiera agregar controles específicos de aplicaciones para esto en la sección Desarrollo de Settings.app ...
KennyDeriemaeker
3
@KennyDeriemaeker :-) ¡Apple podría responder que se supone que debemos usar dispositivos dedicados para las pruebas! Para mí, los efectos secundarios de restablecer mi teléfono normal habrían sido bastante desafortunados. Cambiar la identificación del paquete era una alternativa razonablemente indolora. Recordé cambiarlo antes de
enviarlo
8
¡Solo asegúrate de no hacer un reinicio completo! Simplemente restablecer la configuración de Privacidad y Ubicación (iOS 8) será suficiente.
Canucklesandwich
81

Swift 4 y más reciente

Asegurate que:

import AVFoundation

El siguiente código verifica todos los posibles estados de permiso:

let cameraMediaType = AVMediaType.video
let cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: cameraMediaType)
    
switch cameraAuthorizationStatus {
case .denied: break
case .authorized: break
case .restricted: break

case .notDetermined:
    // Prompting user for the permission to use the camera.
    AVCaptureDevice.requestAccess(for: cameraMediaType) { granted in
        if granted {
            print("Granted access to \(cameraMediaType)")
        } else {
            print("Denied access to \(cameraMediaType)")
        }
    }
}

Desde iOS 10, debe especificar la NSCameraUsageDescriptionclave en su Info.plist para poder solicitar acceso a la cámara, de lo contrario, su aplicación se bloqueará en el tiempo de ejecución. Consulte las API que requieren descripciones de uso .


Como nota al margen interesante, ¿sabía que iOS mata la aplicación si se está ejecutando mientras cambia los permisos de la cámara en Configuración?

Del foro de desarrolladores de Apple:

El sistema realmente mata su aplicación si el usuario alterna el acceso de su aplicación a la cámara en Configuración. Lo mismo se aplica a cualquier clase de datos protegida en la sección Configuración → Privacidad.

Nikita Kukushkin
fuente
¿Qué hay de autorizar la cámara y la galería de fotos?
Hajar ELKOUMIKHI
4

Solución rápida

extension AVCaptureDevice {
    enum AuthorizationStatus {
        case justDenied
        case alreadyDenied
        case restricted
        case justAuthorized
        case alreadyAuthorized
        case unknown
    }

    class func authorizeVideo(completion: ((AuthorizationStatus) -> Void)?) {
        AVCaptureDevice.authorize(mediaType: AVMediaType.video, completion: completion)
    }

    class func authorizeAudio(completion: ((AuthorizationStatus) -> Void)?) {
        AVCaptureDevice.authorize(mediaType: AVMediaType.audio, completion: completion)
    }

    private class func authorize(mediaType: AVMediaType, completion: ((AuthorizationStatus) -> Void)?) {
        let status = AVCaptureDevice.authorizationStatus(for: mediaType)
        switch status {
        case .authorized:
            completion?(.alreadyAuthorized)
        case .denied:
            completion?(.alreadyDenied)
        case .restricted:
            completion?(.restricted)
        case .notDetermined:
            AVCaptureDevice.requestAccess(for: mediaType, completionHandler: { (granted) in
                DispatchQueue.main.async {
                    if granted {
                        completion?(.justAuthorized)
                    } else {
                        completion?(.justDenied)
                    }
                }
            })
        @unknown default:
            completion?(.unknown)
        }
    }
}

Y luego, para usarlo, lo haces

AVCaptureDevice.authorizeVideo(completion: { (status) in
   //Your work here
})
Josh Bernfeld
fuente
¿Qué hay de autorizar la cámara y la galería de fotos?
Hajar ELKOUMIKHI
2

Como una adición a la respuesta de @Raptor, se debe mencionar lo siguiente. Puede recibir el siguiente error a partir de iOS 10:This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.

Para solucionar esto, asegúrese de manejar los resultados del hilo principal de la siguiente manera (Swift 3):

private func showCameraPermissionPopup() {
    let cameraMediaType = AVMediaTypeVideo
    let cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(forMediaType: cameraMediaType)

    switch cameraAuthorizationStatus {
    case .denied:
        NSLog("cameraAuthorizationStatus=denied")
        break
    case .authorized:
        NSLog("cameraAuthorizationStatus=authorized")
        break
    case .restricted:
        NSLog("cameraAuthorizationStatus=restricted")
        break
    case .notDetermined:
        NSLog("cameraAuthorizationStatus=notDetermined")

        // Prompting user for the permission to use the camera.
        AVCaptureDevice.requestAccess(forMediaType: cameraMediaType) { granted in
            DispatchQueue.main.sync {
                if granted {
                    // do something
                } else {
                    // do something else
                }
            }
        }
    }
}
Bart van Kuik
fuente
En caso de denegación, el método requestAccess no funcionará. Deberá mostrar manualmente una alerta para pedirle al usuario que vaya a la configuración y otorgue permisos.
Abdullah Umer
0

Especifique la clave NSCameraUsageDescription en Info.plist primero. Luego marque AVAuthorizationStatus si está autorizado y presente el UIImagePickerController Funcionará.

Yogendra Singh
fuente
-4

Swift: utilizando AVFoundation

  1. Agregue AVFoundation a Target -> Build Fases -> Link Binary with Libraries.
  2. importar AVFoundation en ViewController.
  3. En Info.plist, agregue lo siguiente:

ingrese la descripción de la imagen aquí

  1. En el controlador de vista:

@IBAction func cameraButtonClicked (remitente: AnyObject) {

let authorizationStatus = AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo)
print(authorizationStatus.rawValue)

if AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo) ==  AVAuthorizationStatus.Authorized{
    self.openCameraAfterAccessGrantedByUser()
}
else
{
    print("No Access")

    dispatch_async(dispatch_get_main_queue()) { [unowned self] in
        AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo, completionHandler: { (granted :Bool) -> Void in
            if granted == true
            {
                // User granted
                self.openCameraAfterAccessGrantedByUser()
            }
            else
            {
                // User Rejected
                  alertToEncourageCameraAccessWhenApplicationStarts()
            }
        });
    }
}


//Open camera

    func openCameraAfterAccessGrantedByUser()
    {
    if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera)){
        self.cameraAndGalleryPicker!.sourceType = UIImagePickerControllerSourceType.Camera
        cameraAndGalleryPicker?.delegate = self
        cameraAndGalleryPicker?.allowsEditing =  false
        cameraAndGalleryPicker!.cameraCaptureMode = .Photo
        cameraAndGalleryPicker!.modalPresentationStyle = .FullScreen
        presentViewController(self.cameraAndGalleryPicker!, animated: true, completion: nil)
    }
    else
    {

    }
}

//Show Camera Unavailable Alert

func alertToEncourageCameraAccessWhenApplicationStarts()
    {
        //Camera not available - Alert
        let cameraUnavailableAlertController = 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)
cameraUnavailableAlertController .addAction(settingsAction)
cameraUnavailableAlertController .addAction(cancelAction)
self.window?.rootViewController!.presentViewController(cameraUnavailableAlertController , animated: true, completion: nil)
}
AG
fuente
¿Qué pasa con las entradas de Info.plist? ¿Fuente?
Bill