Rileva l'autorizzazione della fotocamera in iOS


143

Sto sviluppando un'app video molto semplice. Uso il controllo ufficiale: UIImagePickerController.

Ecco il problema Quando si presenta UIImagePickerController per la prima volta, iOS chiederà l'autorizzazione. L'utente può fare clic su Sì o No. Se l'utente fa clic su no, il controllo non viene ignorato. Invece, se l'utente continua a fare clic sul pulsante di avvio, i timer si accendono mentre lo schermo è sempre nero e l'utente non può arrestare i timer o tornare indietro. L'unica cosa che l'utente può fare è uccidere l'app. La prossima volta che viene presentato UIImagePickerController, è ancora una schermata nera e l'utente non può tornare indietro se fa clic su Start.

Mi chiedevo se fosse un bug. Esiste un modo per rilevare l'autorizzazione della telecamera in modo da poter decidere di mostrare UIImagePickerController o no?


Ri: è un bug? Imho, penso di sì, perché ciò che sembra accadere è che il VC sta visualizzando i dati dall'hardware, ma il sistema operativo sta praticamente inviando aria morta. Come iOS è arrivato qui è probabilmente un effetto collaterale dell'evoluzione della famiglia di prodotti. UIImageViewControllerviene notato come aggiunto in iOS 2.0 e i documenti non vengono mai annotati per riflettere che dovrebbe essere utilizzato AVAuthorizationStatus, ma risiede in un altro framework.
benc

Risposte:


229

Controllare AVAuthorizationStatuse gestire le custodie correttamente.

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
}

19
richiede anche: #import <AVFoundation/AVFoundation.h>o simile
toblerpwn

9
Un suggerimento forse utile: se stai testando il codice che lo utilizza, non puoi semplicemente eliminare l'app dal dispositivo di test e quindi reinstallarla. In questo modo iOS non invierà nuovamente la richiesta all'utente! Quello che ha funzionato per me è cambiare Bundle IDl'app ogni volta che voglio testarlo. Un dolore nel sedere, ma almeno qualcosa. Ricordati di
reimpostare

25
@Benjohn: la modifica dell'ID pacchetto non è necessaria. Puoi andare su Impostazioni> Generali> Ripristina e trovare un'impostazione che ripristinerà tutte le richieste di autorizzazione sul dispositivo. Certo, è anche fastidioso in quanto influisce anche su tutte le altre app sul tuo dispositivo. Se solo Apple potesse aggiungere controlli specifici per app per questo nella sezione Sviluppo di Settings.app ...
KennyDeriemaeker,

3
@KennyDeriemaeker :-) Apple potrebbe rispondere che dovremmo usare dispositivi dedicati per i test! Per me gli effetti collaterali del ripristino del mio telefono normale sarebbero stati abbastanza schizzinosi. La modifica dell'ID bundle è stata un'alternativa ragionevolmente indolore. Mi sono ricordato di cambiarlo anche prima
dell'invio

8
Assicurati di non eseguire un ripristino completo! Sarà sufficiente ripristinare le impostazioni di Privacy e posizione (iOS 8).
Canucklesandwich,

81

Swift 4 e versioni successive

Assicurati che:

import AVFoundation

Il codice seguente controlla tutti i possibili stati di autorizzazione:

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

Da iOS 10 devi specificare la NSCameraUsageDescriptionchiave nel tuo Info.plist per poter chiedere l'accesso alla videocamera, altrimenti la tua app andrà in crash durante l'esecuzione. Consulta le API che richiedono descrizioni d'uso .


Come nota a margine interessante, sapevi che iOS uccide l'app se è in esecuzione mentre cambi le autorizzazioni della fotocamera in Impostazioni?

Dal forum degli sviluppatori Apple:

Il sistema uccide effettivamente la tua app se l'utente commuta l'accesso della tua app alla telecamera in Impostazioni. Lo stesso vale per qualsiasi classe di dati protetta nella sezione Impostazioni → Privacy.


Che dire di autorizzare la fotocamera e la galleria fotografica ??
Hajar ELKOUMIKHI,

4

Soluzione rapida

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

E poi per usarlo lo fai

AVCaptureDevice.authorizeVideo(completion: { (status) in
   //Your work here
})

Che dire di autorizzare la fotocamera e la galleria fotografica ??
Hajar ELKOUMIKHI,

2

In aggiunta alla risposta di @Raptor, è necessario menzionare quanto segue. È possibile che venga visualizzato il seguente errore a partire da 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.

Per risolvere questo problema, assicurati di gestire i risultati dal thread principale come segue (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
                }
            }
        }
    }
}

In caso di rifiuto, il metodo requestAccess non funzionerà. Dovrai mostrare manualmente un avviso per chiedere all'utente di accedere alle impostazioni e concedere le autorizzazioni.
Abdullah Umer,

0

Specificare prima la chiave NSCameraUsageDescription in Info.plist. Quindi controllare AVAuthorizationStatus se autorizzato quindi presentare UIImagePickerController. Funzionerà.


-4

Swift: utilizzo di AVFoundation

  1. Aggiungi AVFoundation alla destinazione -> Crea fasi -> Collega binario con librerie.
  2. importare AVFoundation su ViewController.
  3. In Info.plist, aggiungi quanto segue:

inserisci qui la descrizione dell'immagine

  1. On View Controller:

@IBAction func cameraButtonClicked (mittente: 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)
}

Cosa c'è con le voci di Info.plist? Fonte?
Bill
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.