Detectar permissão da câmera no iOS

143

Estou desenvolvendo um aplicativo de vídeo muito simples. Eu uso o controle oficial: UIImagePickerController.

Aqui está o problema. Ao apresentar o UIImagePickerController pela primeira vez, o iOS solicitará a permissão. O usuário pode clicar em sim ou não. Se o usuário clicar em não, o controle não será descartado. Em vez disso, se o usuário continuar clicando no botão Iniciar, os cronômetros continuarão enquanto a tela estiver sempre preta e o usuário não poderá parar os cronômetros ou voltar. A única coisa que o usuário pode fazer é matar o aplicativo. Na próxima vez que o UIImagePickerController for apresentado, ele ainda será uma tela preta e o usuário não poderá voltar se clicar em iniciar.

Eu queria saber se é um bug. Existe alguma maneira de detectar a permissão da câmera para decidirmos mostrar o UIImagePickerController ou não?

user418751
fonte
Re: é um bug? IMHO, acho que sim, porque o que parece acontecer é que o VC está exibindo os dados do hardware, mas o sistema operacional está basicamente enviando ar morto. Como o iOS chegou aqui é provavelmente um efeito colateral da evolução da família de produtos. UIImageViewControlleré anotado como sendo adicionado no iOS 2.0 e os documentos nunca anotados para refletir que o AVAuthorizationStatus deve ser usado, mas reside em outra estrutura.
benc 4/07

Respostas:

229

Verifique AVAuthorizationStatuse manuseie os casos corretamente.

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
fonte
19
também exige: #import <AVFoundation/AVFoundation.h>ou similar
toblerpwn
9
Uma dica possivelmente útil - se você estiver testando o código que usa isso, não poderá simplesmente excluir seu aplicativo do dispositivo de teste e reinstalá-lo. Fazer isso não fará com que o iOS reenvie a solicitação ao usuário! O que funcionou para mim é alterar Bundle IDo aplicativo toda vez que eu quiser testar isso. Uma dor no bumbum, mas alguma coisa, pelo menos. Lembre-se de definir o ID de volta quando tiver terminado ;-) #
930 Benjohn
25
@ Benjohn: alterar o ID do pacote é desnecessário. Você pode ir para Configurações> Geral> Redefinir e encontrar uma configuração que redefinirá todas as solicitações de permissão no dispositivo. É verdade que isso também é irritante, pois afeta todos os outros aplicativos do seu dispositivo. Se apenas a Apple poderia adicionar controles específicos de aplicativos para isso na seção Desenvolvimento de Settings.app ...
KennyDeriemaeker
3
@KennyDeriemaeker :-) A Apple pode responder que devemos usar dispositivos dedicados para testes! Para mim, os efeitos colaterais de redefinir meu telefone comum teriam sido bem ruins. Alterar o ID do pacote era uma alternativa razoavelmente indolor. Lembrei-me para mudá-lo de volta antes da apresentação também :-)
Benjohn
8
Apenas certifique-se de não fazer uma redefinição completa! Basta redefinir as configurações de Privacidade e localização (iOS 8).
Canucklesandwich
81

Swift 4 e mais recente

Tenha certeza de:

import AVFoundation

O código abaixo verifica todos os possíveis estados de permissão:

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 o iOS 10, você precisa especificar a NSCameraUsageDescriptionchave no seu Info.plist para poder solicitar acesso à câmera, caso contrário, seu aplicativo falhará em tempo de execução. Consulte APIs que exigem descrições de uso .


Como uma observação interessante, você sabia que o iOS mata o aplicativo se ele estiver sendo executado enquanto você altera as permissões da câmera em Configurações?

No fórum do desenvolvedor da Apple:

O sistema realmente mata seu aplicativo se o usuário alternar o acesso do aplicativo à câmera em Configurações. O mesmo se aplica a qualquer classe de dados protegida na seção Configurações → Privacidade.

Nikita Kukushkin
fonte
Que tal autorizar câmera e galeria de fotos?
Hajar ELKOUMIKHI
4

Solução Swift

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 então, para usá-lo, você faz

AVCaptureDevice.authorizeVideo(completion: { (status) in
   //Your work here
})
Josh Bernfeld
fonte
Que tal autorizar câmera e galeria de fotos?
Hajar ELKOUMIKHI
2

Como complemento à resposta do @Raptor, o seguinte deve ser mencionado. Você pode receber o seguinte erro começando no 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 corrigir isso, lembre-se de lidar com os resultados do thread principal da seguinte maneira (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
fonte
Em caso de negação, o método requestAccess não funcionará. Você terá que mostrar manualmente um alerta para solicitar ao usuário que vá para configurações e conceda permissões.
Abdullah Umer
0

Especifique a chave NSCameraUsageDescription no Info.plist primeiro. Em seguida, verifique AVAuthorizationStatus se autorizado e apresente o UIImagePickerController. Vai funcionar.

Yogendra Singh
fonte
-4

Swift: Usando AVFoundation

  1. Adicione a AVFoundation ao Target -> Build Fhases -> Link Binary with Libraries.
  2. importar AVFoundation no ViewController.
  3. No Info.plist, adicione o seguinte:

insira a descrição da imagem aqui

  1. No controlador de exibição:

@IBAction func cameraButtonClicked (remetente: 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
fonte
O que há com as entradas Info.plist? Fonte?
Bill