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 rejeté. Au lieu de cela, si l'utilisateur continue de cliquer sur le bouton de démarrage, les minuteries sont allumées alors que l'écran est toujours noir et l'utilisateur ne peut ni les arrêter ni revenir en arrière. La seule chose que l'utilisateur puisse faire est de tuer l'application. La prochaine fois que UIImagePickerController est présenté, l'écran reste noir et l'utilisateur ne peut pas revenir en arrière s'il clique 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 de décider de montrer ou non le UIImagePickerController?
Vérifiez la AVAuthorizationStatus
et gérez les cas correctement.
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
}
Depuis iOS 10, vous devez spécifier la clé NSCameraUsageDescription
dans votre Info.plist pour pouvoir demander un accès à la caméra. Sinon, votre application se bloquera au moment de l'exécution. Voir API nécessitant des descriptions d'utilisation .
Assurez-vous de:
import AVFoundation
Le code Swift 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)")
}
}
}
let cameraMediaType = AVMediaTypeVideo
let cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(forMediaType: 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(forMediaType: cameraMediaType) { granted in
if granted {
print("Granted access to \(cameraMediaType)")
} else {
print("Denied access to \(cameraMediaType)")
}
}
}
Comme note intéressante, saviez-vous qu'iOS tue l'application si elle est en cours d'exécution pendant que vous modifiez ses autorisations d'appareil photo dans les paramètres?
Depuis le forum Apple Developer:
Le système tue réellement votre application si l'utilisateur change l'accès de votre application à caméra dans les paramètres. Il en va de même pour toute classe de données protégée dans la section Paramètres → Confidentialité.
En complément de la réponse de @Raptor, il convient de mentionner les éléments suivants. Le message d'erreur suivant commençant par iOS 10 peut s'afficher: 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
}
}
}
}
}
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
})
Spécifiez d'abord la clé NSCameraUsageDescription dans Info.plist. Puis vérifiez si AVAuthorizationStatus est autorisé puis présentez le UIImagePickerController. Ça va marcher.