在iOS中检测相机权限

160

我正在开发一个非常简单的视频应用程序。我使用官方控件:UIImagePickerController。

问题在于,当首次展示UIImagePickerController时,iOS会询问是否允许访问相机。用户可以选择允许或拒绝。如果用户选择了拒绝,控件不会被关闭。接着,如果用户一直点击开始按钮,计时器会继续运行,但屏幕始终是黑色的,用户无法停止计时器或返回。用户只能强行关闭应用程序。下一次展示UIImagePickerController时,仍然是黑屏,用户无法通过点击开始按钮返回。

我想知道这是否是一个bug。是否有办法来检测相机访问权限,以便我们可以决定是否显示UIImagePickerController?


回复:这是一个错误吗? 在我看来,我认为是的,因为似乎发生的情况是 VC 显示来自硬件的数据,但操作系统基本上发送了死气。iOS 如何到达这里可能是产品系列演变的副作用。UIImageViewController被注释为在 iOS 2.0 中添加,并且文档从未注明应该使用 AVAuthorizationStatus,但是它位于另一个框架中。 - benc
苹果似乎在这里有一个官方教程:https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/requesting_authorization_for_media_capture_on_ios - ch271828n
6个回答

244

检查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
}

20
还需要添加:#import <AVFoundation/AVFoundation.h> 或类似的内容。 - toblerpwn
9
一个可能有用的提示——如果您正在测试使用此代码的应用程序,您不能仅仅删除测试设备上的应用程序然后重新安装。这样做不会导致iOS重新向用户发出请求!但对我而言有效的方法是每次想要测试时更改应用程序的“Bundle ID”。虽然麻烦,但至少有点作用。只需记得在测试完成后将ID设置回来即可;-) - Benjohn
25
@Benjohn:更改 Bundle ID 是不必要的。您可以前往 设置 > 通用 > 重置 并找到一个设置,该设置将重置设备上的所有权限提示。当然,这也很烦人,因为它也会影响设备上的所有其他应用程序。如果只有苹果公司能够在“设置”应用程序的“开发”部分中添加特定于应用程序的控件就好了... - KennyDeriemaeker
3
@KennyDeriemaeker :-) 苹果可能回复说我们应该使用专用设备进行测试!对我来说,重置我的普通手机的副作用会非常糟糕。更改 Bundle Id 是一个相对轻松的替代方案。我也记得在提交之前改回来了 :-) - Benjohn
8
请确保不要进行完全重置!只需重置隐私和定位设置(适用于iOS 8)即可。 - Canucklesandwich
显示剩余6条评论

94

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后,你需要在你的Info.plist文件中指定NSCameraUsageDescription键才能请求相机访问权限,否则你的应用程序将在运行时崩溃。请参见需要使用说明的APIs


来自Apple开发者论坛的有趣侧记:

如果用户在设置中切换了对您应用程序的相机访问权限,系统会实际上终止您的应用程序。对于任何受保护的数据类别,在“设置→隐私”部分也是如此。


授权相机和照片库怎么样? - Hajar ELKOUMIKHI

6

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

然后为了使用它,你需要执行以下操作:

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

授权相机和照片库怎么样? - Hajar ELKOUMIKHI

4
作为对@Raptor答案的补充,需要提到以下内容。从iOS 10开始,您可能会收到以下错误:此应用程序正在从后台线程修改自动布局引擎,而在从主线程访问引擎之后。这可能导致引擎损坏和奇怪的崩溃。 要解决此问题,请确保按以下方式处理主线程的结果(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
                }
            }
        }
    }
}

1
如果被拒绝,requestAccess方法将无法工作。你必须手动显示一个警告来要求用户前往设置并授予权限。 - Abdullah Umer

0
首先在 Info.plist 中指定 NSCameraUsageDescription 键。 然后检查 AVAuthorizationStatus 是否已授权,如果是,则呈现 UIImagePickerController。它会起作用。

-4

Swift: 使用 AVFoundation

  1. 将 AVFoundation 添加到 Target -> Build Phases -> Link Binary with Libraries 中。
  2. 在 ViewController 中导入 AVFoundation。
  3. 在 Info.plist 中添加以下内容:

enter image description here

  • 关于视图控制器:
  • @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)
    }
    

    Info.plist的条目是什么意思?源代码在哪里? - Bill

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接