I am working on an iOS app, in which at one point the user takes a picture. However, I want the user to ONLY be able to use the front facing camera. That is, they should not have access to the rear camera. Currently, my code looks like this:
#IBAction func takePicture(sender: UIButton) {
if UIImagePickerController.availableCaptureModesForCameraDevice(.Front) != nil {
let imagePickerController = UIImagePickerController()
imagePickerController.sourceType = .Camera
imagePickerController.allowsEditing = false
imagePickerController.cameraDevice = .Front
imagePickerController.delegate = self
presentViewController(imagePickerController, animated: true, completion: nil)
}
else {
let alertVC = UIAlertController(
title: "No Camera",
message: "Sorry, this device has no camera",
preferredStyle: .Alert)
let okAction = UIAlertAction(
title: "OK",
style:.Default,
handler: nil)
alertVC.addAction(okAction)
presentViewController(alertVC,
animated: true,
completion: nil)
}
}
Although this defaults the camera to be front facing, it still offers users the choice to switch to the rear camera. Is there a way I can restrict this functionality?
Thanks in advance
If you don't want to allow the user the freedom offered by a UIImagePickerController, simply don't use UIImagePickerController. Devise your own picture-taking interface (by way of AVFoundation), where you are in total control.
Related
In my project I tried to add two things:
A camera source for a person's avatar.
add permission for the camera and photo library access in the info.plist file.
But when I tried to test my app in a real device the app doesn’t show any permission, and when I chose the camera the app exit without error message in the log console?
For the info.plist I use this two line:
<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) camera use</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>$(PRODUCT_NAME) photo use</string>
And add a photo I use this code:
#objc func addNewPerson(){
let picker = UIImagePickerController()
let ac = UIAlertController(title: "Choose image", message: nil, preferredStyle: .actionSheet)
let camera = UIAlertAction(title: "Camera", style: .default){
[weak self] _ in
if UIImagePickerController.isSourceTypeAvailable(.camera) {
picker.allowsEditing = false
picker.delegate = self
picker.sourceType = .camera
self?.present(picker,animated: true)
}else{
let alert = UIAlertController(title: "Warning", message: "You don't have camera", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil)
}
}
ac.addAction(camera)
let galerie = UIAlertAction(title: "Photo library", style: .default){
[weak self] _ in
picker.allowsEditing = true
picker.delegate = self
self?.present(picker,animated: true)
}
ac.addAction(galerie)
ac.addAction(UIAlertAction(title: "Cancel", style: .cancel))
present(ac,animated: true)
}
Thank you.
in your addNewPerson() method before showing the alert you should check if the user already granted access to his Camera you can do that by :
if AVCaptureDevice.AVCaptureDevice.authorizationStatus(AVMediaTypeVideo) == AVAuthorizationStatus.Authorized {
// Already Authorized
} else {
AVCaptureDevice. requestAccess(AVMediaTypeVideo, completionHandler: { (granted: Bool) -> Void in
if granted == true {
// User granted
} else {
// User rejected
}
})
}
and before that make sure you added
Just to mention, in iOS 11 camera access is not required to use the UIImagePicker
Here is copy-pasted fragment from rickster's post.
in iOS 11, UIImagePickerController runs as a separate process from your app. That means:
Your app can't see the user's whole Photos library — it gets read-only access just for whichever asset(s) the user chooses in the image picker.
Because of (1), your app doesn't need the standard privacy authorization for Photos library access. The user explicitly chooses a
specific asset (or multiple) for use in your app, which means the user is granting your app permission to read the asset(s) in question.
If user dennied the camera access, i am showing one alert with cancel and setting button to show it. But the code is not working.
#IBAction func ProfileImageButton(_ sender: UIButton) {
print("profile image Button is pressed")
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
profileimgbool = true
let actionSheet = UIAlertController(title: "Photo Source", message: "choose a Source", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Camera", style: .default, handler: {(action:UIAlertAction) in imagePickerController.sourceType = .camera
self.present(imagePickerController, animated: true, completion: nil)
}))
actionSheet.addAction(UIAlertAction(title: "Photo Library", style: .default, handler: {(action:UIAlertAction) in imagePickerController.sourceType = .photoLibrary
self.present(imagePickerController, animated: true, completion: nil)}))
actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(actionSheet, animated: true, completion: nil)
}
func checkCameraPermission() {
let cameraMediaType = AVMediaType.video
AVCaptureDevice.requestAccess(for: cameraMediaType) { granted in
if granted {
//Do operation
print("Granted access for camera")
// self.setCamera()
} else {
self.noCameraFound()
print("Denied access for camera ")
}
}
}
func noCameraFound(){
let alert = UIAlertController(title: "CallDoc", message: "Please allow camera access in phone settings", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Back", style: UIAlertActionStyle.cancel, handler: {(action:UIAlertAction) in
}));
alert.addAction(UIAlertAction(title: "Open setting", style: UIAlertActionStyle.default, handler: {(action:UIAlertAction) in
UIApplication.shared.open(NSURL(string:UIApplicationOpenSettingsURLString)! as URL, options: [:], completionHandler: nil)
}));
self.present(alert, animated: true, completion: nil)
}
in my above code my method was checkCameraPermission where i will call this to show alert. I needs to show when user click camera, and when if user denied that black screen will show instead of camera. There i need to show that alert pop up.
Where i can call this checkCameraPermission method to show my popup ?.
Any idea ?
for reference purpose I taken the answer from this tutorial.
step 1
add the avfoundation framework in your project
import AVFoundation
step 2
dont forget to Set Camera Usage Description in Info.plist
When you request permission to use the device’s camera, a short message will appear in the default iOS system dialog. You customize this message by adding the Privacy - Camera Usage Description key to your Info.plist file.
step 3
on your image profile change button action verify the permission, etc.
#IBAction func ProfileImageButton(_ sender: UIButton) {
let cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: .video)
switch cameraAuthorizationStatus {
case .notDetermined: requestCameraPermission()
case .authorized: presentCamera()
case .restricted, .denied: alertCameraAccessNeeded()
}
}
based on the above action the condition will satisfy,
If the user has never responded to a request to access his/her camera, you need to prompt with the iOS system alert to request permission:
func requestCameraPermission() {
AVCaptureDevice.requestAccess(for: .video, completionHandler: {accessGranted in
guard accessGranted == true else { return }
self.presentCamera()
})
}
there after the camera access will continue
func presentCamera() {
let photoPicker = UIImagePickerController()
photoPicker.sourceType = .camera
photoPicker.delegate = self as? UIImagePickerControllerDelegate & UINavigationControllerDelegate
self.present(photoPicker, animated: true, completion: nil)
}
To use the image that the camera captured, you need to set up your view controller to adhere to and implement couple of delegate protocols:
class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
// ...
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let photo = info[UIImagePickerControllerOriginalImage] as! UIImage
// do something with the photo... set to UIImageView, save it, etc.
dismiss(animated: true, completion: nil)
}
If camera access has been denied or restricted, you can alert the user and direct them to the Settings app to make the appropriate permissions adjustment:
func alertCameraAccessNeeded() {
let settingsAppURL = URL(string: UIApplicationOpenSettingsURLString)!
let alert = UIAlertController(
title: "Need Camera Access",
message: "Camera access is required to make full use of this app.",
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.open(settingsAppURL, options: [:], completionHandler: nil)
}))
present(alert, animated: true, completion: nil)
}
I am trying to access the photo library for a new app that I am writing. I have an actionSheet that allows the user to choose between their camera and their photo library. When opening the camera, it asks the user for permission but when opening the photo library it does not. Despite this, it still brings the user to their photo library. I specifically referenced it in my info.plist, but there is still no difference. Any help? My code:
#IBAction func allowAccessToPhotos(_ sender: Any) {
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
let actionSheet = UIAlertController(title: "Photo Source", message: "Choose a Source", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Photo Library", style: .default, handler: { (action:UIAlertAction) in imagePickerController.sourceType = .photoLibrary
self.present(imagePickerController, animated: true, completion: nil)
}))
actionSheet.addAction(UIAlertAction(title: "Camera", style: .default, handler: { (action:UIAlertAction) in imagePickerController.sourceType = .camera
self.present(imagePickerController, animated: true, completion: nil)
}))
actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(actionSheet, animated: true, completion: nil)
}
As per UIImagePickerController behaviour, It never gives dialogue to the user for Photos access. UIImagePickerController only ask for the Camera permission.
You have to ask for Photos permission manually to the user. By using the below code you can ask the user for Photos permission.
import Photos
#IBAction func allowAccessToPhotos(_ sender: Any) {
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
let actionSheet = UIAlertController(title: "Photo Source", message: "Choose a Source", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Photo Library", style: .default, handler: { (action:UIAlertAction) in imagePickerController.sourceType = .photoLibrary
let photoAuthorizationStatus = PHPhotoLibrary.authorizationStatus()
switch photoAuthorizationStatus {
case .authorized:
self.present(imagePickerController, animated: true, completion: nil)
case .notDetermined:
PHPhotoLibrary.requestAuthorization({
(newStatus) in
DispatchQueue.main.async {
if newStatus == PHAuthorizationStatus.authorized {
self.present(imagePickerController, animated: true, completion: nil)
}else{
print("User denied")
}
}})
break
case .restricted:
print("restricted")
break
case .denied:
print("denied")
break
}}))
actionSheet.addAction(UIAlertAction(title: "Camera", style: .default, handler: { (action:UIAlertAction) in imagePickerController.sourceType = .camera
self.present(imagePickerController, animated: true, completion: nil)
}))
}
Please refer reference.
Important Note:
If you're not asking for Photos permission to the user then It will cause rejection by the apple team. It depends on your luck, Sometimes the apple team ignore it and sometimes reject our app.
From iOS 11, UIImagePickerController is running remotely on a separate process.
So, your app doesn't need the standard privacy authorization for Photos library access, it gets read-only access just for whichever asset(s) the user chooses in the imagePicker.
To add new asset into photo-library you need NSPhotoLibraryAddUsageDescription in your Info.plist - forum thread
yes you can access photos by PhotosUI library without permission
import PhotosUI
var config = PHPickerConfiguration()
config.selectionLimit = 4
config.filter = .images
let vc = PHPickerViewController(configuration: config)
vc.delegate = self
self.present(vc, animated: true)
extension UIViewController: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
var images = [UIImage]()
for image in results {
if image.itemProvider.canLoadObject(ofClass: UIImage.self) {
image.itemProvider.loadObject(ofClass: UIImage.self) { (newImage, error) in
if let error = error {
print(error.localizedDescription)
} else {
self.images.append(newImage as! UIImage)
}
}
} else {
print("Loaded Assest is not a Image")
}
}
picker.dismiss(animated: true)
}
}
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)
}
I have an app that lets a user take a picture with the camera. I would like to only allow the user to take landscape photos, not portrait. Is there a way for me to lock the camera orientation?
Below is the code I am using:
func takePhoto() {
//If a camera is available on this device, go to camera. If not, present an error message.
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera){
//Create the image picker controller
imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .Camera
//Present the camera view
presentViewController(imagePicker, animated: true, completion: nil)
}
else {
let alert = UIAlertController(title: "Error", message: "There is no camera available", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: {(alertAction) in alert.dismissViewControllerAnimated(true, completion: nil)}))
self.presentViewController(alert, animated: true, completion: nil)
}
}
Is there a way for me to lock the camera orientation
The camera is a physical object. Its orientation has nothing to do with your app. The only way to "lock the camera orientation" is to reach out and grab the user's wrist so that s/he can't turn the device.