Détecter l'autorisation de la caméra dans iOS

143

Je développe une application vidéo très simple. J'utilise le contrôle officiel: UIImagePickerController.

Voici le problème. Lors de la présentation de UIImagePickerController pour la première fois, iOS demandera l'autorisation. L'utilisateur peut cliquer sur oui ou non. Si l'utilisateur clique sur non, le contrôle n'est pas ignoré. Au lieu de cela, si l'utilisateur continue de cliquer sur le bouton de démarrage, les minuteries continuent alors que l'écran est toujours noir, et l'utilisateur ne peut pas arrêter les minuteries ou revenir en arrière. La seule chose que l'utilisateur peut faire est de tuer l'application. La prochaine fois que l'UIImagePickerController est présenté, il s'agit toujours d'un écran noir et l'utilisateur ne peut pas revenir en arrière en cliquant sur Démarrer.

Je me demandais si c'était un bug. Existe-t-il un moyen de détecter l'autorisation de la caméra afin que nous puissions décider d'afficher le UIImagePickerController ou non?

user418751
la source
Re: est-ce un bug? À mon humble avis, je pense que oui, car ce qui semble se produire, c'est que le VC affiche les données du matériel, mais le système d'exploitation envoie essentiellement de l'air mort. La façon dont iOS est arrivé ici est probablement un effet secondaire de l'évolution de la famille de produits. UIImageViewControllerest noté comme étant ajouté dans iOS 2.0, et la documentation n'a jamais été annotée pour indiquer que AVAuthorizationStatus doit être utilisé, mais vit dans un autre cadre.
benc le

Réponses:

229

Vérifiez AVAuthorizationStatuset manipulez correctement les boîtiers.

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
}
Raptor
la source
19
nécessite également: #import <AVFoundation/AVFoundation.h>ou similaire
toblerpwn
9
Un conseil peut-être utile - si vous testez du code qui l'utilise, vous ne pouvez pas simplement supprimer votre application du périphérique de test, puis la réinstaller. Cela n'entraînera pas iOS de réémettre la demande à l'utilisateur! Ce qui a fonctionné pour moi, c'est de changer Bundle IDl'application à chaque fois que je veux tester cela. Une douleur dans les fesses, mais quelque chose, au moins. N'oubliez pas de
réinitialiser
25
@Benjohn: changer l'ID du bundle n'est pas nécessaire. Vous pouvez accéder à Paramètres> Général> Réinitialiser et trouver un paramètre qui réinitialisera toutes les invites d'autorisation sur l'appareil. Certes, c'est également ennuyeux car cela affecte également toutes les autres applications de votre appareil. Si seulement Apple pouvait ajouter des contrôles spécifiques aux applications pour cela dans la section Développement de Settings.app ...
KennyDeriemaeker
3
@KennyDeriemaeker :-) Apple pourrait répondre que nous sommes censés utiliser des appareils dédiés pour les tests! Pour moi, les effets secondaires de la réinitialisation de mon téléphone ordinaire auraient été assez nuls. Changer l'identifiant du bundle était une alternative raisonnablement indolore. Je me suis souvenu de le changer avant la soumission aussi :-)
Benjohn
8
Assurez-vous simplement de ne pas effectuer une réinitialisation complète! Il suffit de réinitialiser les paramètres de confidentialité et de localisation (iOS 8).
Canucklesandwich
81

Swift 4 et plus récent

Assurez-vous de:

import AVFoundation

Le code ci-dessous vérifie tous les états d'autorisation possibles:

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

Depuis iOS 10, vous devez spécifier la NSCameraUsageDescriptionclé dans votre Info.plist pour pouvoir demander l'accès à la caméra, sinon votre application se plantera lors de l'exécution. Voir API nécessitant des descriptions d'utilisation .


En guise de remarque intéressante, saviez-vous qu'iOS tue l'application si elle est en cours d'exécution pendant que vous modifiez les autorisations de sa caméra dans les paramètres?

Depuis le forum des développeurs Apple:

Le système tue réellement votre application si l'utilisateur bascule l'accès de votre application à la caméra dans les paramètres. La même chose s'applique à toute classe de données protégée dans la section Paramètres → Confidentialité.

Nikita Kukushkin
la source
Qu'en est-il de l'autorisation de la caméra et de la galerie de photos?
Hajar ELKOUMIKHI
4

Solution rapide

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

Et puis pour l'utiliser, vous faites

AVCaptureDevice.authorizeVideo(completion: { (status) in
   //Your work here
})
Josh Bernfeld
la source
Qu'en est-il de l'autorisation de la caméra et de la galerie de photos?
Hajar ELKOUMIKHI
2

En plus de la réponse de @Raptor, il convient de mentionner ce qui suit. Vous pouvez recevoir l'erreur suivante à partir d'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.

Pour résoudre ce problème, assurez-vous de gérer les résultats du thread principal comme suit (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
la source
En cas de refus, la méthode requestAccess ne fonctionnera pas. Vous devrez afficher manuellement une alerte pour demander à l'utilisateur d'accéder aux paramètres et d'accorder des autorisations.
Abdullah Umer
0

Spécifiez d'abord la clé NSCameraUsageDescription dans Info.plist. Vérifiez ensuite AVAuthorizationStatus si Authorized, puis présentez UIImagePickerController. Ça va marcher.

Yogendra Singh
la source
-4

Swift: utilisation d'AVFoundation

  1. Ajouter AVFoundation à la cible -> Phases de construction -> Lier le binaire avec les bibliothèques.
  2. importer AVFoundation sur ViewController.
  3. Sur Info.plist, ajoutez ce qui suit:

entrez la description de l'image ici

  1. Sur le contrôleur de vue:

@IBAction func cameraButtonClicked (expéditeur: 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
la source
Qu'est-ce que les entrées Info.plist? La source?
Bill