I have implemented the following code in an app to allow the user to add an image to the app. However the user is never asked for permission to access photo's. What am I missing here?
in my info.plist I have set:
Privacy - Photo Library Usage Description = Please provide access to your photo library
The bundle identifier is:
$(PRODUCT_BUNDLE_IDENTIFIER)
import UIKit
class NewPostXIB: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var image: UIImageView!
let imagePicker = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
imagePicker.delegate = self
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
self.view.endEditing(true)
}
#IBAction func closeButton(_ sender: Any) {
dismiss(animated: false, completion: nil)
}
#IBAction func addImageButton(_ sender: Any) {
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
imagePicker.allowsEditing = true
imagePicker.sourceType = .photoLibrary
present(imagePicker, animated: true, completion: nil)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let pickedImage = info[UIImagePickerControllerEditedImage] as? UIImage {
image.contentMode = .scaleAspectFill
image.image = pickedImage
}
dismiss(animated: false, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
The answer is simple: it's normal behavior since iOS 11, because the UIImagePickerController runs in a separate process and therefore for just read-only access you don't need any special permissions.
According to apple documentation
Note
When using the UIImagePickerController to bring up the user's photo
library, your app doesn't need to request permission explicitly.
Photos automatically prompts the user to request authorization when needed.
Adding the following function solved the problem:
func checkPermission() {
let photoAuthorizationStatus = PHPhotoLibrary.authorizationStatus()
switch photoAuthorizationStatus {
case .authorized:
present(imagePicker, animated: true, completion: nil)
print("Access is granted by user")
case .notDetermined:
PHPhotoLibrary.requestAuthorization({
(newStatus) in
print("status is \(newStatus)")
if newStatus == PHAuthorizationStatus.authorized {
/* do stuff here */
self.present(self.imagePicker, animated: true, completion: nil)
print("success")
}
})
print("It is not determined until now")
case .restricted:
// same same
print("User do not have access to photo album.")
case .denied:
// same same
print("User has denied the permission.")
}
}
Related
I'm trying to implement an ImagePicker on my ViewController, but turns out it presents a blank ImagePicker Controller and UIImagePickerController extension discovery failed with error: (null) message on the console.
I have no idea what I'm doing wrong, and I found no information on the matter. One thing I noticed, though, is that the PickerView takes a little bit too long to be presented.
The code related to the ImagePicker is the following:
private let imagePicker = UIImagePickerController()
private func setupImagePicker() {
imagePicker.sourceType = .photoLibrary
imagePicker.allowsEditing = false
imagePicker.delegate = self
imagePicker.mediaTypes = ["public.image"]
}
#objc private func launchImagePicker() {
let photos = PHPhotoLibrary.authorizationStatus()
if photos == .notDetermined {
PHPhotoLibrary.requestAuthorization({ [weak self] status in
DispatchQueue.main.async {
if status == .authorized, let picker = self?.imagePicker {
self?.present(picker, animated: true, completion: nil)
} else {
self?.present(URealtorUtils.getAlert(message: "You have to authorize photo library access in order to upload a photo"), animated: true, completion: nil)
}
}
})
} else if photos == .authorized {
present(imagePicker, animated: true, completion: nil)
}
}
extension MyController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
self.viewModel.setPropertyImage(image)
}
dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
}
This is a screenshot of what I get when the PickerView is presented:
I checked the details, and there are no issues in your implementation.
I also checked the apple's official sample here.
https://developer.apple.com/documentation/uikit/uiimagepickercontroller/customizing_an_image_picker_controller
Same result!
Finally, this is an issue related to iOS Simulators(version 11.3.1).
FYI, I found iPhone 11 Pro Max didn't work in some previous versions, but iPhone 8 and some others worked.
I just want you guys not wasting time on this. Move ahead.
Thanks.
converted app from swift 3 to swift 4.2.
my app had a upload your profile image feature that is not working anymore and I am trying to figure out why. For now What I see is that
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo
didFinishPickingMediaWithInfo Is not being called after media was chosen
Here is my views full code:
import UIKit
class CameraMenuViewController: BaseViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var UserID:Int = 0
#IBOutlet weak var myImageView: UIImageView!
var callback : ((UIImage) -> ())?
#IBOutlet weak var btn_end: UIButton!
#IBOutlet weak var from_camera: UIBarButtonItem!
#IBOutlet weak var from_gallery: UIBarButtonItem!
#IBAction func btn_end_pressed(_ sender: UIButton) {
//
//self.openViewControllerBasedOnIdentifier("Home")
//let data = UIImagePNGRepresentation(myImageView) as NSData?
let image = myImageView.image!.pngData() as NSData?
//if let data = UIImagePNGRepresentation(myImageView) {
print("callback data")
let userDetails:Dictionary = (UserDefaults.standard.value(forKey: "myUserDetails") as? [String:Any])!
let UserID:Int = userDetails["UserID"] as! Int
print("UserID")
print(UserID)
print("is_selfie from callback")
//save profile image as NewUserID
UserDefaults.standard.set(image, forKey: String(UserID))
UserDefaults.standard.synchronize()
self.view.removeFromSuperview()
//}
}
#IBAction func btn_closer(_ sender: UIButton) {
//self.view.removeFromSuperview()
}
#IBAction func photofromLibrary(_ sender: UIBarButtonItem) {
picker.allowsEditing = false
picker.sourceType = .photoLibrary
picker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary)!
picker.modalPresentationStyle = .popover
present(picker, animated: true, completion: nil)
picker.popoverPresentationController?.barButtonItem = sender
}
#IBAction func shootPhoto(_ sender: UIBarButtonItem) {
if UIImagePickerController.isSourceTypeAvailable(.camera) {
print("photo shoot")
//UserDefaults.standard.set("selfie", forKey: "is_selfie")
UserDefaults.standard.set(true, forKey: "is_selfie")
UserDefaults.standard.synchronize()
DispatchQueue.main.async {
self.picker.allowsEditing = false
self.picker.sourceType = UIImagePickerController.SourceType.camera
self.picker.cameraCaptureMode = .photo
self.picker.modalPresentationStyle = .fullScreen
self.present(self.picker,animated: true,completion: nil)
}
} else {
noCamera()
}
}
let picker = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
picker.delegate = self
DispatchQueue.global(qos: .userInitiated).async
{
self.present(self.picker, animated: true, completion: nil)
}
let language = UserDefaults.standard.object(forKey: "myLanguage") as! String
if(language=="arabic"){
//from_camera.setTitle("كاميرا",for: .normal)
//from_gallery.text.setTitle("الصور",for: .normal)
btn_end.setTitle("إنهاء",for: .normal)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK: - Delegates
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject])
{
let chosenImage = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.originalImage)] as! UIImage //2
myImageView.contentMode = .scaleAspectFit //3
myImageView.image = chosenImage //4
myImageView.layer.borderWidth = 1
myImageView.layer.masksToBounds = false
myImageView.layer.borderColor = UIColor.black.cgColor
myImageView.layer.cornerRadius = myImageView.frame.height/4
myImageView.clipsToBounds = true
callback?(chosenImage)
dismiss(animated:true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
func noCamera(){
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)
present(
alertVC,
animated: true,
completion: nil)
}
}
// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertFromUIImagePickerControllerInfoKey(_ input: UIImagePickerController.InfoKey) -> String {
return input.rawValue
}
Please help me understand why didFinishPickingMediaWithInfo isn't getting called anymore
It appears as though the function declaration changed between Swift 3 and 4.2. This mustn't have been updated for you by the Swift Migrator Tool. One trick I do when this happens, to check what the correct function declaration is, is to use multiline comment syntax to comment out your current function (didFinishPickingMediaWithInfo in your case). Then you can start typing the function out again, and use Xcode autocomplete to ensure you have it correct. You can then copy the contents of the function you commented out to this new and correct function declaration.
Or - you could just look it up the documentation! According to the documentation on imagePickerController, the function should be declared as:
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any])
If you replace your function declaration with the above, it should get called again.
As Craig said you need to change delegate function declaration and also afterwards you need to update the following:
let chosenImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
These two changes combined should solve your issue.
i think the name off the function is update
this is my code in My app And work greate
extension FCViewController: UIImagePickerControllerDelegate
{
internal func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any])
{
if let photo = info[UIImagePickerController.InfoKey.originalImage] as? UIImage
}
// Any Code Here ...
I'm getting the following error:
errors encountered while discovering extensions: Error
Domain=PlugInKit Code=13 "query cancelled"
UserInfo={NSLocalizedDescription=query cancelled}
When using simulator and selecting photo from iPhones stored library....
Can anyone see what might be the issue(s) in the code below?
import UIKit
import Firebase
class PictureViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var descriptionTextField: UITextField!
#IBOutlet weak var nextButton: UIButton!
var imagePicker = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
imagePicker.delegate = self
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
imageView.image = image
imageView.backgroundColor = UIColor.clear
imagePicker.dismiss(animated: true, completion: nil)
}
#IBAction func cameraTapped(_ sender: Any) {
imagePicker.sourceType = .savedPhotosAlbum
imagePicker.allowsEditing = false
present(imagePicker, animated: true, completion: nil)
}
#IBAction func nextTapped(_ sender: Any) {
let imagesFolder = Storage.storage().reference().child("images")
let imageData = UIImagePNGRepresentation(imageView.image!)!
imagesFolder.child("images.png").putData(imageData, metadata: nil, completion: {(metadata, error) in
print("We tried to upload!")
if error != nil {
print("We had an error:\(error)")
} else {
self.performSegue(withIdentifier: "selectUsersegue", sender: nil)
}
})
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
}
}
Try specifying the modalPresentationStyle of the view controller to .overFullScreen
#IBAction func cameraTapped(_ sender: Any) {
imagePicker.sourceType = .savedPhotosAlbum
imagePicker.allowsEditing = false
imagePicker.modalPresentationStyle = .overFullScreen
present(imagePicker, animated: true, completion: nil)
}
Don't know if it will work for you but I guess you should give it a try.
One of the problems could be, that you don't have permissions to access "Photos" on your device. (if this is all of your code, it looks like I am right)
I was having similar issues few weeks ago and adding permission request solved it.
EDIT
// Authorization for Photos
let photos = PHPhotoLibrary.authorizationStatus()
if photos == .notDetermined {
PHPhotoLibrary.requestAuthorization({status in
if status == .authorized{
...
} else {}
})
}
Also make sure you add these to Info.plist
<key>NSPhotoLibraryUsageDescription</key>
<string>Description.</string>
I am just learning to code and I am trying to create a simple code to send a message on IOS. I have made sure to import the MessageUI framework. When I run it on my simulator, it fails and shows THREAD 1 : SIGNAL SIGABRT. I apologize profusely if it's just a small mistake, as I often do such errors and don't wish to waste anyone's time. Here is my code:
import UIKit
import MessageUI
class ViewController: UIViewController, MFMessageComposeViewControllerDelegate {
#IBOutlet weak var messageLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func SendSMS(sender: AnyObject) {
let messageVC = MFMessageComposeViewController()
messageVC.recipients = ["5146276051"]
messageVC.body = messageLabel.text
messageVC.messageComposeDelegate = self
self.presentViewController(messageVC, animated: true, completion: nil)
}
func messageComposeViewController(controller: MFMessageComposeViewController, didFinishWithResult result: MessageComposeResult) {
switch (result.rawValue) {
case MessageComposeResultCancelled.rawValue:
print("Message was cancelled")
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultFailed.rawValue:
print("Message has failed")
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultSent.rawValue:
print("Message was sent")
self.dismissViewControllerAnimated(true, completion: nil)
default:
break
}
}
}
Thank you very much!
Hi try to use your code on physical device, it should work.
BTW in your code please add check whether device support sending messages:
#IBAction func SendSMS(sender: AnyObject) {
//Checking whether device support message sending
if (MFMessageComposeViewController.canSendText() == false) { return }
let messageVC = MFMessageComposeViewController()
messageVC.recipients = ["5146276051"]
messageVC.body = messageLabel.text
messageVC.messageComposeDelegate = self
self.presentViewController(messageVC, animated: true, completion: nil)
}
in that way you will avoid e.g. crashes on simulator.
I am tying to design a view controller that calls a method to load the embedded mail app on the iOS device as soon as the user taps the icon for that view, and every time it's tapped.
But after the mail client is either "Canceled", "Saved", "Sent" etc. The next time the user hits the Tab bar icon for that view controller the view is just a blank screen, it doesn't attempt to load the view again. I tried implementing a function call to loadView() or loadWillAppear() again but it ended up with an infinite loop where every time the mail client ended the client would load again instantly.
Perhaps I included the call a little early? I tried to even include some if statements to catch the program while it's running but it didn't seem to do anything at all. There's a couple places in here where I know it would be an infinite loop or what have you I just can't seem to get it to work. I was also unable to use:
override func viewWillAppear(animated: Bool)
Because the compiler complains about it and parsing boolean values is "Bad".
Anyways, here is the code from my ContactViewControllerTwo.swift
import Foundation
import UIKit
import MessageUI
class ContactViewControllerTwo: UIViewController, MFMailComposeViewControllerDelegate {
var mailControllerDidLoad = false
var mailControllerDismissed = false
override func viewDidLoad() {
viewWillAppear()
}
func viewWillAppear() {
if mailControllerDidLoad == false {
if MFMailComposeViewController.canSendMail() {
let composeVC = MFMailComposeViewController()
composeVC.mailComposeDelegate = self
composeVC.setToRecipients(["123#123.com"])
presentViewController(composeVC, animated: true, completion: nil)
self.presentViewController(composeVC, animated: true, completion: nil)
}
else if mailControllerDidLoad == true && mailControllerDismissed == false{
let composeVC = MFMailComposeViewController()
composeVC.mailComposeDelegate = self
composeVC.setToRecipients(["123#123.com"])
presentViewController(composeVC, animated: true, completion: nil)
self.presentViewController(composeVC, animated: true, completion: nil)
}
else {
print("Cannot send mail - Please try again")
// give feedback to the user
}
}
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if self.isMovingFromParentViewController()
{
print("View controller")
}
else
{
print("New view controller.")
viewWillAppear()
print("Loaded again")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func mailComposeController(controller: MFMailComposeViewController,
didFinishWithResult result: MFMailComposeResult, error: NSError?) {
mailControllerDidLoad = true
switch result.rawValue {
case MFMailComposeResultCancelled.rawValue:
print("Cancelled")
mailControllerDismissed = true
case MFMailComposeResultSaved.rawValue:
print("Saved")
mailControllerDismissed = true
case MFMailComposeResultSent.rawValue:
print("Sent")
mailControllerDismissed = true
case MFMailComposeResultFailed.rawValue:
print("Error: \(error?.localizedDescription)")
mailControllerDismissed = true
default:
mailControllerDismissed = true
break
}
controller.dismissViewControllerAnimated(true, completion: nil)
mailControllerDismissed = true
}
}