Определить разрешение камеры в iOS

146

Я разрабатываю очень простое видео-приложение. Я использую официальный элемент управления: UIImagePickerController.

Вот в чем проблема. При первом представлении UIImagePickerController iOS запросит разрешение. Пользователь может щелкнуть да или нет. Если пользователь нажимает кнопку «Нет», элемент управления не закрывается. Вместо этого, если пользователь продолжает нажимать кнопку запуска, таймеры продолжаются, пока экран всегда черный, и пользователь не может остановить таймеры или вернуться. Единственное, что может сделать пользователь, - это убить приложение. В следующий раз, когда будет представлен UIImagePickerController, это все еще будет черный экран, и пользователь не сможет вернуться, нажав кнопку «Пуск».

Мне было интересно, не ошибка ли это. Есть ли способ определить разрешение камеры, чтобы мы могли решить, отображать UIImagePickerController или нет?

user418751
источник
Re: это ошибка? IMHO, я так думаю, потому что, похоже, происходит то, что VC отображает данные с оборудования, но ОС в основном отправляет мертвый воздух. То, как здесь появилась iOS, вероятно, является побочным эффектом эволюции семейства продуктов. UIImageViewControllerотмечен как добавленный в iOS 2.0, и документы никогда не аннотируются, чтобы отразить, что AVAuthorizationStatus следует использовать, но живет в другой структуре.
Benc
У Apple, похоже, есть официальное руководство здесь: developer.apple.com/documentation/avfoundation/…
ch271828n

Ответы:

232

Проверьте AVAuthorizationStatusи правильно обработайте ящики.

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
источник
19
также требует: #import <AVFoundation/AVFoundation.h>или аналогичный
toblerpwn 01
9
Возможно, полезный совет - если вы тестируете код, который использует это, вы не можете просто удалить свое приложение с тестового устройства, а затем повторно установить. Это не заставит iOS повторно отправить запрос пользователю! Что сработало для меня, так это менять Bundle IDприложение каждый раз, когда я хочу это проверить. Боль в заднице, но хоть что-то. Просто не забудьте вернуть идентификатор, когда закончите ;-)
Benjohn
25
@Benjohn: менять идентификатор пакета не нужно. Вы можете перейти в « Настройки»> «Основные»> «Сброс» и найти параметр, который сбрасывает все запросы разрешений на устройстве. Конечно, это также раздражает, поскольку это влияет и на все другие приложения на вашем устройстве. Если бы только Apple могла добавить для этого элементы управления для конкретных приложений в разделе «Разработка» в Settings.app ...
KennyDeriemaeker
3
@KennyDeriemaeker :-) Apple может ответить, что мы должны использовать специальные устройства для тестирования! Для меня побочные эффекты сброса обычного телефона были бы довольно отстойными. Изменение идентификатора пакета было достаточно безболезненной альтернативой. Я тоже не
забыл
8
Только убедитесь, что вы не делаете полный сброс! Достаточно просто сбросить настройки конфиденциальности и местоположения (iOS 8).
Canucklesandwich
83

Swift 4 и новее

Убедитесь, что:

import AVFoundation

В приведенном ниже коде проверяются все возможные состояния разрешений:

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

Начиная с iOS 10, вам необходимо указать NSCameraUsageDescriptionключ в Info.plist, чтобы иметь возможность запрашивать доступ к камере, иначе ваше приложение выйдет из строя во время выполнения. См. API-интерфейсы, требующие описания использования .


В качестве интересного примечания: знаете ли вы, что iOS убивает приложение, если оно работает, пока вы меняете разрешения камеры в настройках?

С форума разработчиков Apple:

Система фактически убивает ваше приложение, если пользователь переключает доступ вашего приложения к камере в настройках. То же самое относится к любому защищенному классу данных в разделе Настройки → Конфиденциальность.

Никита Кукушкин
источник
А как насчет авторизации камеры и фотогалереи ??
Хаджар ЭЛЬКОУМИХИ
4

Быстрое решение

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

А затем, чтобы использовать это, вы делаете

AVCaptureDevice.authorizeVideo(completion: { (status) in
   //Your work here
})
Джош Бернфельд
источник
А как насчет авторизации камеры и фотогалереи ??
Хаджар ЭЛЬКОУМИХИ
3

В дополнение к ответу @Raptor следует упомянуть следующее. Вы можете получить следующую ошибку, начиная с 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.

Чтобы исправить это, убедитесь, что вы обрабатываете результаты из основного потока следующим образом (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
                }
            }
        }
    }
}
Барт ван Куик
источник
В случае отказа метод requestAccess работать не будет. Вам нужно будет вручную показать предупреждение, чтобы попросить пользователя перейти в настройки и предоставить разрешения.
Абдулла Умер
0

Сначала укажите ключ NSCameraUsageDescription в Info.plist. Затем проверьте AVAuthorizationStatus, если авторизован, затем представьте UIImagePickerController. Это будет работать.

Йогендра Сингх
источник
-4

Swift: использование AVFoundation

  1. Добавьте AVFoundation в Target -> Build Phases -> Link Binary with Libraries.
  2. импортировать AVFoundation в ViewController.
  3. В Info.plist добавьте следующее:

введите описание изображения здесь

  1. На контроллере просмотра:

@IBAction func cameraButtonClicked (отправитель: 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
источник
Что с записями Info.plist? Источник?
Билл