When my app tries to access the camera for the first time on iOS 8, the user is presented with a camera permission dialog, much like the microphone one for microphone access in iOS 7.
In iOS 7, it was possible to invoke the microphone permission dialog beforehand and see if the permission was granted (see this question, for example). Is there a similar way to invoke the camera permission dialog in iOS 8? Can the dialog be combined for microphone AND camera access permission?
Here is the approach we ended up using:
if ([AVCaptureDevice respondsToSelector:#selector(requestAccessForMediaType: completionHandler:)]) {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
// Will get here on both iOS 7 & 8 even though camera permissions weren't required
// until iOS 8. So for iOS 7 permission will always be granted.
if (granted) {
// Permission has been granted. Use dispatch_async for any UI updating
// code because this block may be executed in a thread.
dispatch_async(dispatch_get_main_queue(), ^{
[self doStuff];
});
} else {
// Permission has been denied.
}
}];
} else {
// We are on iOS <= 6. Just do what we need to do.
[self doStuff];
}
I'm running into a similar issue, if the user has denied camera access when they are first prompted, pressing the button to take snapshot results in a black screen in the camera mode.
However I want to detect that the user has declined access and prompt them it must be turned on but I can't find any functions to check the current user camera access, is there such a function?
EDIT: The following check will let you know in IOS 8 about camera access:
#import <AVFoundation/AVFoundation.h>
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if(status == AVAuthorizationStatusAuthorized) { // authorized
}
else if(status == AVAuthorizationStatusDenied){ // denied
}
else if(status == AVAuthorizationStatusRestricted){ // restricted
}
else if(status == AVAuthorizationStatusNotDetermined){ // not determined
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if(granted){ // Access has been granted ..do something
} else { // Access denied ..do something
}
}];
}
This information was found on the following question (How to know that application have camera access or not programmatically in iOS8):
Here is my Swift Solution (iOS 8), I needed the camera for QR scanning so really had to prompt its use.
This provides
Encourage the user to select allow if prior to the default allow camera access question
Easy way to access settings if the user denied the first request.
To get it running call check camera in ViewDidAppear / or ViewDidLoad etc. I needed to use viewDidAppear so my custom camera views constraints were set up.
func checkCamera() {
let authStatus = AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo)
switch authStatus {
case .authorized: break // Do your stuff here i.e. allowScanning()
case .denied: alertToEncourageCameraAccessInitially()
case .notDetermined: alertPromptToAllowCameraAccessViaSetting()
default: alertToEncourageCameraAccessInitially()
}
}
func alertToEncourageCameraAccessInitially() {
let alert = UIAlertController(
title: "IMPORTANT",
message: "Camera access required for QR Scanning",
preferredStyle: UIAlertControllerStyle.alert
)
alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
alert.addAction(UIAlertAction(title: "Allow Camera", style: .cancel, handler: { (alert) -> Void in
UIApplication.shared.openURL(URL(string: UIApplicationOpenSettingsURLString)!)
}))
present(alert, animated: true, completion: nil)
}
func alertPromptToAllowCameraAccessViaSetting() {
let alert = UIAlertController(
title: "IMPORTANT",
message: "Please allow camera access for QR Scanning",
preferredStyle: UIAlertControllerStyle.alert
)
alert.addAction(UIAlertAction(title: "Dismiss", style: .cancel) { alert in
if AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo).count > 0 {
AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo) { granted in
DispatchQueue.main.async() {
self.checkCamera() } }
}
}
)
present(alert, animated: true, completion: nil)
}
Thanks to jamix above for the tip for using dispatch_async - makes the response to show the newly set camera function so much faster.
Sorry for a mix of trailing closures.. wanted to try them out.
None of the answers seem to check for both microphone and camera permissions. Our code checks against the scenario where camera permissions are granted but microphone access is denied.
Since we're new to Swift, it's unlikely the gnarly nested closures and if statements are optimal. Please share suggestions for improving the code! But at least it works so far in testing.
AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo, completionHandler: { (videoGranted: Bool) -> Void in
if (videoGranted) {
AVCaptureDevice.requestAccessForMediaType(AVMediaTypeAudio, completionHandler: { (audioGranted: Bool) -> Void in
if (audioGranted) {
dispatch_async(dispatch_get_main_queue()) {
// Both video & audio granted
}
} else {
// Rejected audio
}
})
} else {
// Rejected video
}
})
Swift 3.0 Solution
import AVFoundation
Note: add Privacy - Camera Usage Description key on your Info.plist
//MARK: Camera Handling
func callCamera(){
let myPickerController = UIImagePickerController()
myPickerController.delegate = self;
myPickerController.sourceType = UIImagePickerControllerSourceType.camera
self.present(myPickerController, animated: true, completion: nil)
NSLog("Camera");
}
func checkCamera() {
let authStatus = AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo)
switch authStatus {
case .authorized: callCamera() // Do your stuff here i.e. callCameraMethod()
case .denied: alertToEncourageCameraAccessInitially()
case .notDetermined: alertPromptToAllowCameraAccessViaSetting()
default: alertToEncourageCameraAccessInitially()
}
}
func alertToEncourageCameraAccessInitially() {
let alert = UIAlertController(
title: "IMPORTANT",
message: "Camera access required for capturing photos!",
preferredStyle: UIAlertControllerStyle.alert
)
alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
alert.addAction(UIAlertAction(title: "Allow Camera", style: .cancel, handler: { (alert) -> Void in
UIApplication.shared.openURL(URL(string: UIApplicationOpenSettingsURLString)!)
}))
present(alert, animated: true, completion: nil)
}
func alertPromptToAllowCameraAccessViaSetting() {
let alert = UIAlertController(
title: "IMPORTANT",
message: "Camera access required for capturing photos!",
preferredStyle: UIAlertControllerStyle.alert
)
alert.addAction(UIAlertAction(title: "Dismiss", style: .cancel) { alert in
if AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo).count > 0 {
AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo) { granted in
DispatchQueue.main.async() {
self.checkCamera() } }
}
}
)
present(alert, animated: true, completion: nil)
}
For Swift 3, you can add this on your viewWillAppear method of your first view controller:
First import the AVFoundation framework
import AVFoundation
Then:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let authorizationStatus = AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo)
switch authorizationStatus {
case .notDetermined:
AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo) { granted in
if granted {
print("access granted")
}
else {
print("access denied")
}
}
case .authorized:
print("Access authorized")
case .denied, .restricted:
print("restricted")
}
}
Don't forget to add Privacy - Camera Usage Description key on your Info.plist
For me this work on iOS7 and iOS8:
ALAuthorizationStatus status = [ALAssetsLibrary authorizationStatus];
switch (status) {
case ALAuthorizationStatusAuthorized:
break;
case ALAuthorizationStatusRestricted:
case ALAuthorizationStatusDenied:
break;
case ALAuthorizationStatusNotDetermined:
break;
}
I make an access check on the app delegate.
import UIKit
import AVFoundation
import Photos
func applicationDidBecomeActive(application: UIApplication) {
cameraAllowsAccessToApplicationCheck()
internetAvailabilityOnApplicationCheck()
photoLibraryAvailabilityCheck()
}
//MARK:- CAMERA ACCESS CHECK
func cameraAllowsAccessToApplicationCheck()
{
let authorizationStatus = AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo)
switch authorizationStatus {
case .NotDetermined:
// permission dialog not yet presented, request authorization
AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo,
completionHandler: { (granted:Bool) -> Void in
if granted {
print("access granted")
}
else {
print("access denied")
}
})
case .Authorized:
print("Access authorized")
case .Denied, .Restricted:
alertToEncourageCameraAccessWhenApplicationStarts()
default:
print("DO NOTHING")
}
}
//MARK:- PHOTO LIBRARY ACCESS CHECK
func photoLibraryAvailabilityCheck()
{
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.Authorized
{
}
else
{
var cameraUnavailableAlertController = UIAlertController (title: "Photo Library Unavailable", message: "Please check to see if device settings doesn't allow photo library access", preferredStyle: .Alert)
var settingsAction = UIAlertAction(title: "Settings", style: .Destructive) { (_) -> Void in
let settingsUrl = NSURL(string:UIApplicationOpenSettingsURLString)
if let url = settingsUrl {
UIApplication.sharedApplication().openURL(url)
}
}
var cancelAction = UIAlertAction(title: "Okay", style: .Default, handler: nil)
cameraUnavailableAlertController .addAction(settingsAction)
cameraUnavailableAlertController .addAction(cancelAction)
self.window?.rootViewController!.presentViewController(cameraUnavailableAlertController , animated: true, completion: nil)
}
}
func internetAvailabilityOnApplicationCheck()
{
//MARK:- INTERNET AVAILABLITY
if InternetReachability.isConnectedToNetwork() {
}
else
{
dispatch_async(dispatch_get_main_queue(), {
//INTERNET NOT AVAILABLE ALERT
var internetUnavailableAlertController = UIAlertController (title: "Network Unavailable", message: "Please check your internet connection settings and turn on Network Connection", preferredStyle: .Alert)
var settingsAction = UIAlertAction(title: "Settings", style: .Destructive) { (_) -> Void in
let settingsUrl = NSURL(string:UIApplicationOpenSettingsURLString)
if let url = settingsUrl {
UIApplication.sharedApplication().openURL(url)
}
}
var cancelAction = UIAlertAction(title: "Okay", style: .Default, handler: nil)
internetUnavailableAlertController .addAction(settingsAction)
internetUnavailableAlertController .addAction(cancelAction)
self.window?.rootViewController!.presentViewController(internetUnavailableAlertController , animated: true, completion: nil)
})
}
}
*
The issue for me was that Bundle name and Bundle Display Name were not getting set in my Info.plist due to some recent build configuration changes. Kind of an unlikely case... But it took me a few hours to nail this down. Hopefully it helps for someone else.
Related
What is the most effective way to prompt a User to provide access to the Camera (or other feature), while ensuring the best experience?
When accessing the Camera, iOS must ask the Customer permission to allow access. As we all know, if the Customer says "No" but then changes their mind, there is no way to reverse this decision from within your App. They must go to Settings and follow a number of steps to re-enable access, namely:
Settings -> Privacy -> Camera -> [Your App] -> turn switch on
Permission Priming is an effective way to avoid a situation where your Customer might deny access to a key feature of your app.
On iOS an App is only allowed to trigger the default system permission once per feature. Permission priming is when an app "primes" the Customer with an alert that mimics a system permission.
The benefit to doing this is so that if the Customer opts-out (selects Cancel), the App is still able to ask again in future, until they say yes — at which time the actual system permission is displayed and the Customer is statistically less likely to then change their mind and enter into the negative work flow.
Furthermore, since cameraSelected() performs this workflow, if the user declines, but then at some future point does change their settings, the App will immediately reflect the new permissions without further input (ie. the User could switch to Settings, change permissions, and then switch back to the App).
Here is some Swift 3 code to implement this feature:
[UPDATE: Included is a solution to open a deep-link to Settings where the User can enable camera access, if they have previously denied it.]
[UPDATE 2: Added sample lines for Analytics implementation.]
func cameraSelected() {
// First we check if the device has a camera (otherwise will crash in Simulator - also, some iPod touch models do not have a camera).
if let deviceHasCamera = UIImagePickerController.isSourceTypeAvailable(.camera) {
let authStatus = AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo)
switch authStatus {
case .authorized:
showCameraPicker()
case .denied:
alertPromptToAllowCameraAccessViaSettings()
case .notDetermined:
permissionPrimeCameraAccess()
default:
permissionPrimeCameraAccess()
}
} else {
let alertController = UIAlertController(title: "Error", message: "Device has no camera", preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: { (alert) in
Analytics.track(event: .permissionsPrimeCameraNoCamera)
})
alertController.addAction(defaultAction)
present(alertController, animated: true, completion: nil)
}
}
func alertPromptToAllowCameraAccessViaSettings() {
let alert = UIAlertController(title: "\"<Your App>\" Would Like To Access the Camera", message: "Please grant permission to use the Camera so that you can <customer benefit>.", preferredStyle: .alert )
alert.addAction(UIAlertAction(title: "Open Settings", style: .cancel) { alert in
Analytics.track(event: .permissionsPrimeCameraOpenSettings)
if let appSettingsURL = NSURL(string: UIApplicationOpenSettingsURLString) {
UIApplication.shared.openURL(appSettingsURL)
}
})
present(alert, animated: true, completion: nil)
}
func permissionPrimeCameraAccess() {
let alert = UIAlertController( title: "\"<Your App>\" Would Like To Access the Camera", message: "<Your App> would like to access your Camera so that you can <customer benefit>.", preferredStyle: .alert )
let allowAction = UIAlertAction(title: "Allow", style: .default, handler: { (alert) -> Void in
Analytics.track(event: .permissionsPrimeCameraAccepted)
if AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo).count > 0 {
AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { [weak self] granted in
DispatchQueue.main.async {
self?.cameraSelected() // try again
}
})
}
})
alert.addAction(allowAction)
let declineAction = UIAlertAction(title: "Not Now", style: .cancel) { (alert) in
Analytics.track(event: .permissionsPrimeCameraCancelled)
}
alert.addAction(declineAction)
present(alert, animated: true, completion: nil)
}
func showCameraPicker() {
let picker = UIImagePickerController()
picker.delegate = self
picker.modalPresentationStyle = UIModalPresentationStyle.currentContext
picker.allowsEditing = false
picker.sourceType = UIImagePickerControllerSourceType.camera
present(picker, animated: true, completion: nil)
}
Suppose we have two buttons (one for picking picture from library another from camera) with tags 1,2 that are linked to action:
import UIKit
import AVFoundation
#IBAction func changeImage(sender: UIButton) {
let picker = UIImagePickerController()
if sender.tag == 2 { // tag = 2 for camera button. tag = 1 for image picker
guard UIImagePickerController.isSourceTypeAvailable(.camera) else { return }
let cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: .video)
switch cameraAuthorizationStatus {
case .notDetermined:
requestCameraPermission()
return
case .authorized:
break
case .restricted, .denied:
alertCameraAccessNeeded()
return
#unknown default:
return
}
picker.sourceType = .camera
}
picker.allowsEditing = true
picker.delegate = self
present(picker, animated: true)
}
private func requestCameraPermission() {
AVCaptureDevice.requestAccess(for: .video) { [weak self] accessGranted in
if !accessGranted {
DispatchQueue.main.async {
self?.alertCameraAccessNeeded()
}
}
}
}
private func alertCameraAccessNeeded() {
guard let settingsAppURL = URL(string: UIApplication.openSettingsURLString),
UIApplication.shared.canOpenURL(settingsAppURL) else { return } // This should never happen
let alert = UIAlertController(
title: "Need Camera Access",
message: "Camera access is required to take pictures of item.",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "Cancel", style: .default))
alert.addAction(UIAlertAction(title: "Allow Camera", style: .cancel) { _ in
UIApplication.shared.open(settingsAppURL, options: [:])
})
present(alert, animated: true)
}
If the user did not allow access to photo album at the start, I will prompt with a pop up with Cancel and Settings to choose from. If he chooses settings, it will bring him to settings page where he can enable camera and photo Library for the app. However, as soon as the user toggles the camera or photo library switch in settings, my app crashes with "Message from debugger: Terminated due to signal 9" printout. Below is the code for my popup
#IBAction func cameraBarBtnPress(sender: AnyObject) {
let photoAuthStatus = PHPhotoLibrary.authorizationStatus()
switch photoAuthStatus {
case .Authorized:
presentFusumaCameraVC()
case .Denied, .Restricted :
showNeedPhotoAlbumAccessPopup()
case .NotDetermined:
PHPhotoLibrary.requestAuthorization({ (authStatus: PHAuthorizationStatus) in
switch authStatus {
case .Authorized:
self.presentFusumaCameraVC()
case .Denied, .Restricted :
self.showNeedPhotoAlbumAccessPopup()
case .NotDetermined:
print("Shouldnt get to here")
}
})
}
}
func showNeedPhotoAlbumAccessPopup() {
let alertController = UIAlertController(title: "Enable Photo Album Access", message: "", preferredStyle: .Alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .Default, handler: nil)
let settingsAction = UIAlertAction(title: "Settings", style: .Default, handler: { (action: UIAlertAction) in
let settingsUrl = NSURL(string: UIApplicationOpenSettingsURLString)
if let url = settingsUrl {
UIApplication.sharedApplication().openURL(url)
}
})
alertController.addAction(settingsAction)
alertController.addAction(cancelAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
What would be the correct way to handle this so that the user can go back to the app and begin selecting the photos after toggling the switch?
Apple's documentation says the following:
If permissions changes, app is quit
Background task expiration handler is called, if registered
iOS then kills the application
Haven't seen a way around so far.
Trying to write this:
if usergavepermissiontousercamera
opencamera
else
showmycustompermissionview
Couldn't find a current way to do this simple task.
Note: Should also work iOS7 even if it requires a different method
You can use the following code for doing the same:
if AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo) == AVAuthorizationStatus.Authorized {
// Already Authorized
} else {
AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo, completionHandler: { (granted: Bool) -> Void in
if granted == true {
// User granted
} else {
// User rejected
}
})
}
NOTE:
Make sure that you add the AVFoundation Framework in the Link Binary section of build phases
You should write import AVFoundation on your class for importing AVFoundation
SWIFT 3
if AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo) == AVAuthorizationStatus.authorized {
// Already Authorized
} else {
AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { (granted: Bool) -> Void in
if granted == true {
// User granted
} else {
// User Rejected
}
})
}
Swift 4
if AVCaptureDevice.authorizationStatus(for: .video) == .authorized {
//already authorized
} else {
AVCaptureDevice.requestAccess(for: .video, completionHandler: { (granted: Bool) in
if granted {
//access allowed
} else {
//access denied
}
})
}
The following is a cleaned up answer updated for Swift 4.x:
Starting with iOS 10, you must also request permission in the info.plist file to avoid a crash:
Privacy - Camera Usage Description
You must provide a string that is presented to the user with this key. Failure to do so will result in a crash when attempting to access the camera.
import AVFoundation
func checkCameraAccess() {
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .denied:
print("Denied, request permission from settings")
presentCameraSettings()
case .restricted:
print("Restricted, device owner must approve")
case .authorized:
print("Authorized, proceed")
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { success in
if success {
print("Permission granted, proceed")
} else {
print("Permission denied")
}
}
}
}
func presentCameraSettings() {
let alertController = UIAlertController(title: "Error",
message: "Camera access is denied",
preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Cancel", style: .default))
alertController.addAction(UIAlertAction(title: "Settings", style: .cancel) { _ in
if let url = URL(string: UIApplicationOpenSettingsURLString) {
UIApplication.shared.open(url, options: [:], completionHandler: { _ in
// Handle
})
}
})
present(alertController, animated: true)
}
This will test for the four possible answers, and then either request permission if it is notDetermined, or direct the user to settings to enable it if it is denied. If it is restricted, the current user may not be able to enable it, but you should provide some form of guidance to them.
Swift 3.0 Updated Solution
func callCamera(){
let myPickerController = UIImagePickerController()
myPickerController.delegate = self;
myPickerController.sourceType = UIImagePickerControllerSourceType.camera
self.present(myPickerController, animated: true, completion: nil)
NSLog("Camera");
}
func checkCamera() {
let authStatus = AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo)
switch authStatus {
case .authorized: callCamera() // Do your stuff here i.e. callCameraMethod()
case .denied: alertPromptToAllowCameraAccessViaSetting()
case .notDetermined: alertToEncourageCameraAccessInitially()
default: alertToEncourageCameraAccessInitially()
}
}
func alertToEncourageCameraAccessInitially() {
let alert = UIAlertController(
title: "IMPORTANT",
message: "Camera access required for capturing photos!",
preferredStyle: UIAlertControllerStyle.alert
)
alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
alert.addAction(UIAlertAction(title: "Allow Camera", style: .cancel, handler: { (alert) -> Void in
UIApplication.shared.openURL(URL(string: UIApplicationOpenSettingsURLString)!)
}))
present(alert, animated: true, completion: nil)
}
func alertPromptToAllowCameraAccessViaSetting() {
let alert = UIAlertController(
title: "IMPORTANT",
message: "Camera access required for capturing photos!",
preferredStyle: UIAlertControllerStyle.alert
)
alert.addAction(UIAlertAction(title: "Dismiss", style: .cancel) { alert in
if AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo).count > 0 {
AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo) { granted in
DispatchQueue.main.async() {
self.checkCamera() } }
}
}
)
present(alert, animated: true, completion: nil)
}
This will open the camera when permission is given by the user. Otherwise show alert for asking permission.
func openCamera(){
let authStatus = AVCaptureDevice.authorizationStatus(for: AVMediaType.video)
switch (authStatus){
case .notDetermined, .restricted, .denied:
showAlert(title: "Unable to access the Camera", message: "To enable access, go to Settings > Privacy > Camera and turn on Camera access for this app.")
case .authorized:
alert.dismiss(animated: true, completion: nil)
if(UIImagePickerController .isSourceTypeAvailable(.camera)){
picker.sourceType = .camera
picker.showsCameraControls=true
picker.allowsEditing=true
self.viewController!.present(picker, animated: true, completion: nil)
}
}
}
after this call this function for showing alert
func showAlert(title:String, message:String) {
let alert = UIAlertController(title: title,
message: message,
preferredStyle: UIAlertController.Style.alert)
let okAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alert.addAction(okAction)
let settingsAction = UIAlertAction(title: "Settings", style: .default, handler: { _ in
// Take the user to Settings app to possibly change permission.
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString) else { return }
if UIApplication.shared.canOpenURL(settingsUrl) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(settingsUrl, completionHandler: { (success) in
// Finished opening URL
})
} else {
// Fallback on earlier versions
UIApplication.shared.openURL(settingsUrl)
}
}
})
alert.addAction(settingsAction)
self.viewController!.present(alert, animated: true, completion: nil)
}
You can import the AVFoundation framework and use the
authorizationStatus(for:)
method shown below and handle the respective cases.
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized: // The user has previously granted access to the camera.
self.setupCaptureSession()
case .notDetermined: // The user has not yet been asked for camera access.
AVCaptureDevice.requestAccess(for: .video) { granted in
if granted {
self.setupCaptureSession()
}
}
case .denied: // The user has previously denied access.
return
case .restricted: // The user can't grant access due to restrictions.
return
}
I have modified the above answer and removed the initial prompt, since when we want to use device's camera the system is prompting for permissions itself:
func checkPermissions() {
let authStatus = AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo)
switch authStatus {
case .authorized:
setupCamera()
case .denied:
alertPromptToAllowCameraAccessViaSetting()
default:
// Not determined fill fall here - after first use, when is't neither authorized, nor denied
// we try to use camera, because system will ask itself for camera permissions
setupCamera()
}
}
func alertPromptToAllowCameraAccessViaSetting() {
let alert = UIAlertController(title: "Error", message: "Camera access required to...", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .default))
alert.addAction(UIAlertAction(title: "Settings", style: .cancel) { (alert) -> Void in
UIApplication.shared.openURL(URL(string: UIApplicationOpenSettingsURLString)!)
})
present(alert, animated: true)
}
With the new functionality in iOS 8, if you are using a camera in the app, it will ask for permission to access the camera and then when you try to retake the pic, it asks for permission to access photo library. Next time when I launch the app, I wish to check if the camera and photo library has access permissions to it.
For camera, I check it by
if ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] == AVAuthorizationStatusDenied)
{
// do something
}
I am looking for something similar to this for photo library.
I know this has already been answered, but just to expand on #Tim answer, here is the code you need (iOS 8 and above):
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
if (status == PHAuthorizationStatusAuthorized) {
// Access has been granted.
}
else if (status == PHAuthorizationStatusDenied) {
// Access has been denied.
}
else if (status == PHAuthorizationStatusNotDetermined) {
// Access has not been determined.
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if (status == PHAuthorizationStatusAuthorized) {
// Access has been granted.
}
else {
// Access has been denied.
}
}];
}
else if (status == PHAuthorizationStatusRestricted) {
// Restricted access - normally won't happen.
}
Don't forget to #import <Photos/Photos.h>
If you are using Swift 3.0 or higher, you can use the following code:
// Get the current authorization state.
let status = PHPhotoLibrary.authorizationStatus()
if (status == PHAuthorizationStatus.authorized) {
// Access has been granted.
}
else if (status == PHAuthorizationStatus.denied) {
// Access has been denied.
}
else if (status == PHAuthorizationStatus.notDetermined) {
// Access has not been determined.
PHPhotoLibrary.requestAuthorization({ (newStatus) in
if (newStatus == PHAuthorizationStatus.authorized) {
}
else {
}
})
}
else if (status == PHAuthorizationStatus.restricted) {
// Restricted access - normally won't happen.
}
Don't forget to import Photos
Check +[PHPhotoLibrary authorizationStatus] – if not set, it will return PHAuthorizationStatusNotDetermined. (You can then request access using +requestAuthorization: on the same class.)
Just as formality, Swift 2.X version:
func checkPhotoLibraryPermission() {
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .Authorized:
//handle authorized status
case .Denied, .Restricted :
//handle denied status
case .NotDetermined:
// ask for permissions
PHPhotoLibrary.requestAuthorization() { (status) -> Void in
switch status {
case .Authorized:
// as above
case .Denied, .Restricted:
// as above
case .NotDetermined:
// won't happen but still
}
}
}
}
And Swift 3 / Swift 4:
import Photos
func checkPhotoLibraryPermission() {
let status = PHPhotoLibrary.authorizationStatus()
switch status {
case .authorized:
//handle authorized status
case .denied, .restricted :
//handle denied status
case .notDetermined:
// ask for permissions
PHPhotoLibrary.requestAuthorization { status in
switch status {
case .authorized:
// as above
case .denied, .restricted:
// as above
case .notDetermined:
// won't happen but still
}
}
}
}
Here is a complete guide for iOS 8+ (without ALAssetLibrary):
First, we have to provide the usage description as now it's required by PHPhotoLibrary.
For doing this we must open the file info.plist, find the key Privacy - Photo Library Usage Description and provide the value for it. If the key doesn't exist then just create it.
Here is an image for example:
Also make sure that the value of the key Bundle name is not empty in the info.plist file.
Now when we have description, we can normally request authorization by calling requestAuthorization method:
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
switch (status) {
case PHAuthorizationStatusAuthorized:
NSLog(#"PHAuthorizationStatusAuthorized");
break;
case PHAuthorizationStatusDenied:
NSLog(#"PHAuthorizationStatusDenied");
break;
case PHAuthorizationStatusNotDetermined:
NSLog(#"PHAuthorizationStatusNotDetermined");
break;
case PHAuthorizationStatusRestricted:
NSLog(#"PHAuthorizationStatusRestricted");
break;
}
}];
NOTE 1: requestAuthorization actually doesn't show alert on every call. It shows once per some time, saves user's answer and returns it everytime instead of showing alert again. But as it isn't what we need, here is a useful code which always shows alert every time we need permission (with redirection to settings):
- (void)requestAuthorizationWithRedirectionToSettings {
dispatch_async(dispatch_get_main_queue(), ^{
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
if (status == PHAuthorizationStatusAuthorized)
{
//We have permission. Do whatever is needed
}
else
{
//No permission. Trying to normally request it
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if (status != PHAuthorizationStatusAuthorized)
{
//User don't give us permission. Showing alert with redirection to settings
//Getting description string from info.plist file
NSString *accessDescription = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"NSPhotoLibraryUsageDescription"];
UIAlertController * alertController = [UIAlertController alertControllerWithTitle:accessDescription message:#"To give permissions tap on 'Change Settings' button" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:#"Cancel" style:UIAlertActionStyleCancel handler:nil];
[alertController addAction:cancelAction];
UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:#"Change Settings" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
}];
[alertController addAction:settingsAction];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}
}];
}
});
}
Common problem 1: Some users complain that the app doesn't show an alert after doing the above-mentioned changes in the info.plist file.
Solution: For testing try to change Bundle Identifier from the project file to something else, clean and rebuild the app. If it started working then everything is fine, rename it back.
Common Problem 2: There is some specific case when fetch results are not updated (and the views which used images from those fetch requests still empty accordingly) when the app gets permissions to photos, while running as it was promised in the documentation.
Actually it happens when we use WRONG code like this:
- (void)viewDidLoad {
if ([PHPhotoLibrary authorizationStatus] != PHAuthorizationStatusAuthorized)
{
//Reloading some view which needs photos
[self reloadCollectionView];
// ...
} else {
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if (status == PHAuthorizationStatusAuthorized)
[self reloadCollectionView];
// ...
}];
}
// ...
}
In this case, if the user denied giving permissions on viewDidLoad then jumped to settings, allowed and jumped back to the the app, views will not be refreshed because [self reloadCollectionView] and fetch requests were not sent.
Solution: We just have to call [self reloadCollectionView] and do other fetch requests before requiring authorization like this:
- (void)viewDidLoad {
//Reloading some view which needs photos
[self reloadCollectionView];
if ([PHPhotoLibrary authorizationStatus] != PHAuthorizationStatusAuthorized)
{
// ...
}
I did it like this:
- (void)requestPermissions:(GalleryPermissions)block
{
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
switch (status)
{
case PHAuthorizationStatusAuthorized:
block(YES);
break;
case PHAuthorizationStatusNotDetermined:
{
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus authorizationStatus)
{
if (authorizationStatus == PHAuthorizationStatusAuthorized)
{
block(YES);
}
else
{
block(NO);
}
}];
break;
}
default:
block(NO);
break;
}
}
And i send in what I need to do as the block depending on success or failure.
iOS 14 onwards Apple has added a new feature that will give limited access to the photos library. Based on your requirements (Example creating custom photo gallery) you have to check if user has given limited access only and wants to give full access.
For backwards compatibility, the old versions without the parameter return .authorized even when you get limited access.
Swift 5:
switch PHPhotoLibrary.authorizationStatus(for: .readWrite) {
case .notDetermined:
// ask for access
case .restricted, .denied:
// sorry
case .authorized:
// we have full access
// new option:
case .limited:
// we only got access to some photos of library
}
There is a code to call limited access screen again. if user has given .limited access only and you want user to select the images again.
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: presentVCObj)
On every restart of app iOS will show alert to notify user about the limited access. If you want to stop that alert then add
PHPhotoLibraryPreventAutomaticLimitedAccessAlert to YES in Info.plist
UPDATE for: SWIFT 3 IOS10
Note: import Photos in AppDelegate.swift as follows
// AppDelegate.swift
import UIKit
import Photos
...
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
photoLibraryAvailabilityCheck()
}
//MARK:- PHOTO LIBRARY ACCESS CHECK
func photoLibraryAvailabilityCheck()
{
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized
{
}
else
{
PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler)
}
}
func requestAuthorizationHandler(status: PHAuthorizationStatus)
{
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.authorized
{
}
else
{
alertToEncouragePhotoLibraryAccessWhenApplicationStarts()
}
}
//MARK:- CAMERA & GALLERY NOT ALLOWING ACCESS - ALERT
func alertToEncourageCameraAccessWhenApplicationStarts()
{
//Camera not available - Alert
let internetUnavailableAlertController = 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 {
DispatchQueue.main.async {
UIApplication.shared.open(url as URL, options: [:], completionHandler: nil) //(url as URL)
}
}
}
let cancelAction = UIAlertAction(title: "Okay", style: .default, handler: nil)
internetUnavailableAlertController .addAction(settingsAction)
internetUnavailableAlertController .addAction(cancelAction)
self.window?.rootViewController!.present(internetUnavailableAlertController , animated: true, completion: nil)
}
func alertToEncouragePhotoLibraryAccessWhenApplicationStarts()
{
//Photo Library not available - Alert
let cameraUnavailableAlertController = UIAlertController (title: "Photo Library Unavailable", message: "Please check to see if device settings doesn't allow photo library access", preferredStyle: .alert)
let settingsAction = UIAlertAction(title: "Settings", style: .destructive) { (_) -> Void in
let settingsUrl = NSURL(string:UIApplicationOpenSettingsURLString)
if let url = settingsUrl {
UIApplication.shared.open(url as URL, options: [:], completionHandler: nil)
}
}
let cancelAction = UIAlertAction(title: "Okay", style: .default, handler: nil)
cameraUnavailableAlertController .addAction(settingsAction)
cameraUnavailableAlertController .addAction(cancelAction)
self.window?.rootViewController!.present(cameraUnavailableAlertController , animated: true, completion: nil)
}
Answer updated from Alvin George
Using ALAssetsLibrary should work:
ALAuthorizationStatus status = [ALAssetsLibrary authorizationStatus];
switch (status) {
case ALAuthorizationStatusNotDetermined: {
// not determined
break;
}
case ALAuthorizationStatusRestricted: {
// restricted
break;
}
case ALAuthorizationStatusDenied: {
// denied
break;
}
case ALAuthorizationStatusAuthorized: {
// authorized
break;
}
default: {
break;
}
}
I have a simple solution on swift 2.0
//
// AppDelegate.swift
// HoneyBadger
//
// Created by fingent on 14/08/15.
// Copyright (c) 2015 fingent. All rights reserved.
//
import UIKit
import Photos
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
self.window?.makeKeyAndVisible()
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewControllerWithIdentifier("LoginPageID")
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
return true
}
func applicationDidEnterBackground(application: UIApplication) {
print("Application On background", terminator: "")
}
func applicationDidBecomeActive(application: UIApplication) {
cameraAllowsAccessToApplicationCheck()
photoLibraryAvailabilityCheck()
}
//MARK:- CAMERA ACCESS CHECK
func cameraAllowsAccessToApplicationCheck()
{
let authorizationStatus = AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo)
switch authorizationStatus {
case .NotDetermined:
// permission dialog not yet presented, request authorization
AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo,
completionHandler: { (granted:Bool) -> Void in
if granted {
print("access granted", terminator: "")
}
else {
print("access denied", terminator: "")
}
})
case .Authorized:
print("Access authorized", terminator: "")
case .Denied, .Restricted:
alertToEncourageCameraAccessWhenApplicationStarts()
default:
print("DO NOTHING", terminator: "")
}
}
//MARK:- PHOTO LIBRARY ACCESS CHECK
func photoLibraryAvailabilityCheck()
{
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.Authorized
{
}
else
{
PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler)
}
}
func requestAuthorizationHandler(status: PHAuthorizationStatus)
{
if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.Authorized
{
}
else
{
alertToEncouragePhotoLibraryAccessWhenApplicationStarts()
}
}
//MARK:- CAMERA & GALLERY NOT ALLOWING ACCESS - ALERT
func alertToEncourageCameraAccessWhenApplicationStarts()
{
//Camera not available - Alert
let internetUnavailableAlertController = 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)
internetUnavailableAlertController .addAction(settingsAction)
internetUnavailableAlertController .addAction(cancelAction)
self.window?.rootViewController!.presentViewController(internetUnavailableAlertController , animated: true, completion: nil)
}
func alertToEncouragePhotoLibraryAccessWhenApplicationStarts()
{
//Photo Library not available - Alert
let cameraUnavailableAlertController = UIAlertController (title: "Photo Library Unavailable", message: "Please check to see if device settings doesn't allow photo library access", preferredStyle: .Alert)
let settingsAction = UIAlertAction(title: "Settings", style: .Destructive) { (_) -> Void in
let settingsUrl = NSURL(string:UIApplicationOpenSettingsURLString)
if let url = settingsUrl {
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)
}
}
Here is a small and simple snippet that I usually use.
- (void)requestPhotoAuthorization:(void (^)(BOOL granted))granted
{
void (^handler)(PHAuthorizationStatus) = ^(PHAuthorizationStatus status)
{
if (status == PHAuthorizationStatusAuthorized) granted(YES);
else if (status == PHAuthorizationStatusNotDetermined) [PHPhotoLibrary requestAuthorization:handler];
else granted(NO);
};
handler([PHPhotoLibrary authorizationStatus]);
}
Swift 2.0+
Based on a combination of answers here, I've created a solution for myself. This method only checks if there is no permission.
We got a method pickVideo() that requires access to photos. If it is not .Authorized ask for permission.
If permission is not given, pickVideo() will not be called, and the user cannot pick a video.
As long as the user did not give full access to photos, you can avoid to let them pick 'or crash' your application.
// Method that requires access to photos
func pickVideo(){
// Check for permission
if PHPhotoLibrary.authorizationStatus() != .Authorized{
// If there is no permission for photos, ask for it
PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler)
return
}
//... pick video code here...
}
func requestAuthorizationHandler(status: PHAuthorizationStatus){
if PHPhotoLibrary.authorizationStatus() == .Authorized{
// The user did authorize, so, pickVideo may be opened
// Ensure pickVideo is called from the main thread to avoid GUI problems
dispatch_async(dispatch_get_main_queue()) {
pickVideo()
}
} else {
// Show Message to give permission in Settings
let alertController = UIAlertController(title: "Error", message: "Enable photo permissions in settings", preferredStyle: .Alert)
let settingsAction = UIAlertAction(title: "Settings", style: .Default) { (alertAction) in
if let appSettings = NSURL(string: UIApplicationOpenSettingsURLString) {
UIApplication.sharedApplication().openURL(appSettings)
}
}
alertController.addAction(settingsAction)
// If user cancels, do nothing, next time Pick Video is called, they will be asked again to give permission
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
alertController.addAction(cancelAction)
// Run GUI stuff on main thread
dispatch_async(dispatch_get_main_queue()) {
self.presentViewController(alertController, animated: true, completion: nil)
}
}
}
I am using this code, but unfortunately it doesn't work.
After a user has denied camera access, I want to ask them for permission to use the camera again the next time they try to load it (in this case it's a barcode scanner using the camera view). I always get AVAuthorizationStatusDenied and then granted always automatically returns NO even though I ask for it again in code.
Many of my users are e-mailing me saying "my screen is black when I try to barcode scan" and it's because they have denied camera access for some reason. I want to be able to prompt them again because most likely the denial was a mistake.
Is there a possible way to do this?
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if(authStatus == AVAuthorizationStatusAuthorized)
{
NSLog(#"%#", #"You have camera access");
}
else if(authStatus == AVAuthorizationStatusDenied)
{
NSLog(#"%#", #"Denied camera access");
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if(granted){
NSLog(#"Granted access to %#", AVMediaTypeVideo);
} else {
NSLog(#"Not granted access to %#", AVMediaTypeVideo);
}
}];
}
else if(authStatus == AVAuthorizationStatusRestricted)
{
NSLog(#"%#", #"Restricted, normally won't happen");
}
else if(authStatus == AVAuthorizationStatusNotDetermined)
{
NSLog(#"%#", #"Camera access not determined. Ask for permission.");
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if(granted){
NSLog(#"Granted access to %#", AVMediaTypeVideo);
} else {
NSLog(#"Not granted access to %#", AVMediaTypeVideo);
}
}];
}
else
{
NSLog(#"%#", #"Camera access unknown error.");
}
After some research it looks like you can't do what I'd like. Here is the alternative that I coded to pop a dialog and open the Settings app automatically if on iOS 8+.
Some notes:
Since iOS 10 you need to specify NSCameraUsageDescription key in your Info.plist to be able ask for camera access, otherwise your app will crash at runtime.
Once the user changes any permissions for your app, it will kill your app. Handle accordingly and save any needed data before the user hits that "Go" button.
At some point between iOS 8 and 11, Apple no longer required the user to touch the Privacy cell in the Setting apps in order to get to and change the Camera settings. You may want to change your instructions on what the user is supposed to do in the Settings app based on what iOS version they are using. If someone wants to leave a comment below telling us all what exact iOS version that changed in, that would be awesome.
As of the last edit of this answer, the code below is working on iOS 14.2.
Swift 5.2:
At the top of your view controller:
import AVFoundation
Before opening the camera view:
#IBAction func goToCamera()
{
let status = AVCaptureDevice.authorizationStatus(for: .video)
switch (status)
{
case .authorized:
self.popCamera()
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { (granted) in
if (granted)
{
self.popCamera()
}
else
{
self.camDenied()
}
}
case .denied:
self.camDenied()
case .restricted:
let alert = UIAlertController(title: "Restricted",
message: "You've been restricted from using the camera on this device. Without camera access this feature won't work. Please contact the device owner so they can give you access.",
preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(okAction)
self.present(alert, animated: true, completion: nil)
#unknown default:
fatalError()
}
}
Denial alert with completion block:
func camDenied()
{
DispatchQueue.main.async
{
var alertText = "It looks like your privacy settings are preventing us from accessing your camera to do barcode scanning. You can fix this by doing the following:\n\n1. Close this app.\n\n2. Open the Settings app.\n\n3. Scroll to the bottom and select this app in the list.\n\n4. Turn the Camera on.\n\n5. Open this app and try again."
var alertButton = "OK"
var goAction = UIAlertAction(title: alertButton, style: .default, handler: nil)
if UIApplication.shared.canOpenURL(URL(string: UIApplication.openSettingsURLString)!)
{
alertText = "It looks like your privacy settings are preventing us from accessing your camera to do barcode scanning. You can fix this by doing the following:\n\n1. Touch the Go button below to open the Settings app.\n\n2. Turn the Camera on.\n\n3. Open this app and try again."
alertButton = "Go"
goAction = UIAlertAction(title: alertButton, style: .default, handler: {(alert: UIAlertAction!) -> Void in
UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)
})
}
let alert = UIAlertController(title: "Error", message: alertText, preferredStyle: .alert)
alert.addAction(goAction)
self.present(alert, animated: true, completion: nil)
}
}
Objective-C:
At the top of your view controller:
#import <AVFoundation/AVFoundation.h>
Before opening the camera view:
- (IBAction)goToCamera
{
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if(authStatus == AVAuthorizationStatusAuthorized)
{
[self popCamera];
}
else if(authStatus == AVAuthorizationStatusNotDetermined)
{
NSLog(#"%#", #"Camera access not determined. Ask for permission.");
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted)
{
if(granted)
{
NSLog(#"Granted access to %#", AVMediaTypeVideo);
[self popCamera];
}
else
{
NSLog(#"Not granted access to %#", AVMediaTypeVideo);
[self camDenied];
}
}];
}
else if (authStatus == AVAuthorizationStatusRestricted)
{
// My own Helper class is used here to pop a dialog in one simple line.
[Helper popAlertMessageWithTitle:#"Error" alertText:#"You've been restricted from using the camera on this device. Without camera access this feature won't work. Please contact the device owner so they can give you access."];
}
else
{
[self camDenied];
}
}
Denial alert:
- (void)camDenied
{
NSLog(#"%#", #"Denied camera access");
NSString *alertText;
NSString *alertButton;
BOOL canOpenSettings = (&UIApplicationOpenSettingsURLString != NULL);
if (canOpenSettings)
{
alertText = #"It looks like your privacy settings are preventing us from accessing your camera to do barcode scanning. You can fix this by doing the following:\n\n1. Touch the Go button below to open the Settings app.\n\n2. Turn the Camera on.\n\n3. Open this app and try again.";
alertButton = #"Go";
}
else
{
alertText = #"It looks like your privacy settings are preventing us from accessing your camera to do barcode scanning. You can fix this by doing the following:\n\n1. Close this app.\n\n2. Open the Settings app.\n\n3. Scroll to the bottom and select this app in the list.\n\n4. Turn the Camera on.\n\n5. Open this app and try again.";
alertButton = #"OK";
}
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Error"
message:alertText
delegate:self
cancelButtonTitle:alertButton
otherButtonTitles:nil];
alert.tag = 3491832;
[alert show];
}
Delegate call for the UIAlertView:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (alertView.tag == 3491832)
{
BOOL canOpenSettings = (&UIApplicationOpenSettingsURLString != NULL);
if (canOpenSettings)
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
}
}
Once they have denied camera access, the user can authorize camera use for your app in Settings. By design, you can't override this in your own code.
You can detect this case with the following sample code and then explain to the user how to fix it: iOS 7 UIImagePickerController Camera No Image
NSString *mediaType = AVMediaTypeVideo; // Or AVMediaTypeAudio
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:mediaType];
// The user has explicitly denied permission for media capture.
else if(authStatus == AVAuthorizationStatusDenied){
NSLog(#"Denied");
}
For Swift 3.0
This will lead the user to settings for changing the permission.
func checkCameraAuthorise() -> Bool {
let status = AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo)
if status == .restricted || status == .denied {
let dialog = ZAlertView(title: "", message: "Please allow access to the camera in the device's Settings -> Privacy -> Camera", isOkButtonLeft: false, okButtonText: "OK", cancelButtonText: "Cancel", okButtonHandler:
{ _ -> Void in UIApplication.shared.openURL(URL(string: UIApplicationOpenSettingsURLString)!)}, cancelButtonHandler: { alertView in alertView.dismissAlertView() })
dialog.show()
return false
}
return true
}
Full code of Camera access and photo library access
import AVFoundation
To handle camera action use the below code:
Method calling
func openCameraOrLibrary(){
let imagePicker = UIImagePickerController()
let alertController : UIAlertController = UIAlertController(title: "Select Camera or Photo Library".localized, message: "", preferredStyle: .actionSheet)
let cameraAction : UIAlertAction = UIAlertAction(title: "Camera".localized, style: .default, handler: {(cameraAction) in
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.camera) == true {
if self.isCamAccessDenied() == false { **//Calling cam access method here**
imagePicker.sourceType = .camera
imagePicker.delegate = self
self.present(imagePicker, animated: true, completion: nil)
}
}else{
self.present(self.showAlert(Title: "", Message: "Camera is not available on this Device or accesibility has been revoked!".localized), animated: true, completion: nil)
self.showTabbar()
}
})
let libraryAction : UIAlertAction = UIAlertAction(title: "Photo Library", style: .default, handler: {(libraryAction) in
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.photoLibrary) == true {
imagePicker.sourceType = .photoLibrary
imagePicker.delegate = self
self.present(imagePicker, animated: true, completion: nil)
}else{
self.showTabbar()
self.present(self.showAlert(Title: "", Message: "Photo Library is not available on this Device or accesibility has been revoked!".localized), animated: true, completion: nil)
}
})
let cancelAction : UIAlertAction = UIAlertAction(title: "Cancel".localized, style: .cancel , handler: {(cancelActn) in
self.showTabbar()
})
alertController.addAction(cameraAction)
alertController.addAction(libraryAction)
alertController.addAction(cancelAction)
alertController.popoverPresentationController?.sourceView = view
alertController.popoverPresentationController?.sourceRect = view.frame
self.present(alertController, animated: true, completion: nil)
self.hideTabbar()
}
Method to handle camera access functionality
func isCamAccessDenied()-> Bool
{
let status = AVCaptureDevice.authorizationStatus(for: AVMediaType.video)
if status == .restricted || status == .denied {
DispatchQueue.main.async
{
var alertText = "It looks like your privacy settings are preventing us from accessing your camera to do barcode scanning. You can fix this by doing the following:\n\n1. Close this app.\n\n2. Open the Settings app.\n\n3. Scroll to the bottom and select this app in the list.\n\n4. Turn the Camera on.\n\n5. Open this app and try again."
var alertButton = "OK"
var goAction = UIAlertAction(title: alertButton, style: .default, handler: nil)
if UIApplication.shared.canOpenURL(URL(string: UIApplicationOpenSettingsURLString)!)
{
alertText = "It looks like your privacy settings are preventing us from accessing your camera to do barcode scanning. You can fix this by doing the following:\n\n1. Touch the Go button below to open the Settings app.\n\n2. Turn the Camera on.\n\n3. Open this app and try again."
alertButton = "OK"
goAction = UIAlertAction(title: alertButton, style: .default, handler: {(alert: UIAlertAction!) -> Void in
UIApplication.shared.open(URL(string: UIApplicationOpenSettingsURLString)!, options: [:], completionHandler: nil)
})
}
let alert = UIAlertController(title: "Error", message: alertText, preferredStyle: .alert)
alert.addAction(goAction)
self.present(alert, animated: true, completion: nil)
}
return true
}
return false
}