Wykryj uprawnienia kamery w iOS

143

Tworzę bardzo prostą aplikację wideo. Używam oficjalnej kontrolki: UIImagePickerController.

Tu jest problem. Podczas prezentowania UIImagePickerController po raz pierwszy system iOS zapyta o pozwolenie. Użytkownik może kliknąć tak lub nie. Jeśli użytkownik kliknie przycisk nie, formant nie zostanie odrzucony. Zamiast tego, jeśli użytkownik ciągle kliknie przycisk Start, liczniki czasu włączają się, gdy ekran jest zawsze czarny, a użytkownik nie może zatrzymać liczników ani wrócić. Jedyne, co użytkownik może zrobić, to zabić aplikację. Następnym razem, gdy UIImagePickerController jest prezentowany, nadal jest to czarny ekran i użytkownik nie może wrócić po kliknięciu przycisku Start.

Zastanawiałem się, czy to błąd. Czy jest jakiś sposób, abyśmy mogli wykryć pozwolenie kamery, abyśmy mogli zdecydować o wyświetleniu UIImagePickerController, czy nie?

user418751
źródło
Re: czy to błąd? Myślę, że tak, ponieważ wydaje mi się, że VC wyświetla dane ze sprzętu, ale system operacyjny w zasadzie wysyła martwe powietrze. To, jak pojawił się iOS, jest prawdopodobnie efektem ubocznym ewolucji rodziny produktów. UIImageViewControllerjest odnotowany jako dodany w iOS 2.0, a dokumenty nigdy nie były opatrzone adnotacjami, aby odzwierciedlić, że należy użyć AVAuthorizationStatus, ale żyje w innej strukturze.
benc

Odpowiedzi:

229

Sprawdź AVAuthorizationStatusi odpowiednio postępuj ze skrzynkami.

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
źródło
19
wymaga również: #import <AVFoundation/AVFoundation.h>lub podobnego
toblerpwn
9
Prawdopodobnie przydatna wskazówka - jeśli testujesz kod, który z tego korzysta, nie możesz po prostu usunąć aplikacji z urządzenia testującego, a następnie ponownie zainstalować. Nie spowoduje to ponownego wysłania żądania do użytkownika przez system iOS! U mnie jednak zadziałało to, że Bundle IDza każdym razem, gdy chcę to przetestować, zmieniałem aplikację. Ból pośladków, ale przynajmniej coś. Pamiętaj tylko, aby
odłożyć
25
@Benjohn: zmiana identyfikatora pakietu nie jest konieczna. Możesz przejść do Ustawienia> Ogólne> Resetuj i znaleźć ustawienie, które zresetuje wszystkie monity o pozwolenie na urządzeniu. To prawda, jest to również denerwujące, ponieważ wpływa również na wszystkie inne aplikacje na twoim urządzeniu. Gdyby tylko Apple mógł dodać do tego kontrolki specyficzne dla aplikacji w sekcji Development w Settings.app ...
KennyDeriemaeker
3
@KennyDeriemaeker :-) Apple może odpowiedzieć, że do testów powinniśmy używać dedykowanych urządzeń! Dla mnie skutki uboczne resetowania mojego zwykłego telefonu byłyby całkiem kiepskie. Zmiana identyfikatora pakietu była rozsądnie bezbolesną alternatywą. Pamiętałem, żeby zmienić to z powrotem przed poddaniem :-)
Benjohn,
8
Tylko upewnij się, że nie wykonujesz pełnego resetu! Wystarczy zresetować ustawienia prywatności i lokalizacji (iOS 8).
Canucklesandwich
81

Swift 4 i nowsze

Upewnij się:

import AVFoundation

Poniższy kod sprawdza wszystkie możliwe stany uprawnień:

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

Od iOS 10 musisz określić NSCameraUsageDescriptionklucz w swoim Info.plist, aby móc poprosić o dostęp do kamery, w przeciwnym razie Twoja aplikacja ulegnie awarii w czasie wykonywania. Zobacz opisy interfejsów API wymagające użycia .


Ciekawostką na marginesie: czy wiesz, że iOS zabija aplikację, jeśli jest uruchomiona, podczas gdy zmieniasz uprawnienia aparatu w Ustawieniach?

Z forum programistów Apple:

System faktycznie zabija twoją aplikację, jeśli użytkownik przełączy dostęp aplikacji do aparatu w Ustawieniach. To samo dotyczy każdej chronionej klasy danych w sekcji Ustawienia → Prywatność.

Nikita Kukushkin
źródło
A co z autoryzacją aparatu i galerii zdjęć?
Hajar ELKOUMIKHI
4

Szybkie rozwiązanie

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

A potem, aby go użyć, robisz

AVCaptureDevice.authorizeVideo(completion: { (status) in
   //Your work here
})
Josh Bernfeld
źródło
A co z autoryzacją aparatu i galerii zdjęć?
Hajar ELKOUMIKHI
2

Jako dodatek do odpowiedzi od @Raptor należy wspomnieć o następujących kwestiach. Zaczynając od systemu iOS 10 może pojawić się następujący błąd: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.

Aby to naprawić, postępuj z wynikami z głównego wątku w następujący sposób (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
źródło
W przypadku odmowy metoda requestAccess nie będzie działać. Będziesz musiał ręcznie wyświetlić alert, aby poprosić użytkownika o przejście do ustawień i przyznanie uprawnień.
Abdullah Umer
0

Najpierw określ klucz NSCameraUsageDescription w Info.plist. Następnie sprawdź AVAuthorizationStatus, jeśli jest autoryzowany, a następnie przedstaw UIImagePickerController. To będzie działać.

Yogendra Singh
źródło
-4

Swift: Korzystanie z AVFoundation

  1. Dodaj AVFoundation do celu -> Fazy kompilacji -> Połącz plik binarny z bibliotekami.
  2. importuj AVFoundation do ViewController.
  3. Na Info.plist dodaj następujące informacje:

wprowadź opis obrazu tutaj

  1. Kontroler podglądu:

@IBAction func cameraButtonClicked (sender: 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
źródło
O co chodzi z wpisami Info.plist? Źródło?
Bill