Reputation: 881
On the surface I thought that this had to be a delegate issue, but after asking for the delegate the right one was returned.
I created an ImagePicker class to handle all the UIImagePickerController stuff. Every thing works until the delegate methods need to be called. After I pick a photo, the imagePicker dismisses, but the didFinishPickingMediaWithInfo
method never gets called. Please help! Thanks :)
func selectPhoto() {
imagePicker.delegate = self //Delegate gets set here
let photoAsk = UIAlertController.init( //Ask user if they want to take picture or choose one
title: "Edit Profile Picture",
message: nil,
preferredStyle: .alert)
let cameraAction = UIAlertAction.init(
title: "Take Photo",
style: .default) { (UIAlertAction) in
if (UIImagePickerController.isSourceTypeAvailable(.camera)) {
self.imagePicker.sourceType = .camera
UIApplication.topViewController()!.present(self.imagePicker, animated: true, completion:nil)
} else {
print("Cannot access camera in simulator.")
return
}
}
let photoLibraryAction = UIAlertAction.init(
title: "Photo Library",
style: .default) { (UIAlertAction) in
self.imagePicker.sourceType = .photoLibrary
UIApplication.topViewController()!.present(self.imagePicker, animated: true, completion:nil)
print("UIImagePickerDelegate: \(self.imagePicker.delegate.debugDescription)") // <--THIS PRINTS OUT "AppName.ImagePicker: 0x145d7bdf0>", and the class name is ImagePicker
}
let cancelAction = UIAlertAction.init(
title: "Cancel",
style: .cancel) { (UIAlertAction) in return }
photoAsk.addAction(cameraAction)
photoAsk.addAction(photoLibraryAction)
photoAsk.addAction(cancelAction)
imagePicker.mediaTypes = [kUTTypeImage as String]
UIApplication.topViewController()?.present(photoAsk, animated: true, completion: nil)
}
}
This never gets called:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print("Image picked.") //NEVER PRINTS
}
Upvotes: 15
Views: 18754
Reputation: 159
swift 4.2
Add Delegate method according ViewController
UIImagePickerControllerDelegate,UINavigationControllerDelegate
//IBOutlet
@IBOutlet weak var ImagePhoto: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
//Button Action Take Photo
@IBAction func btnPhotoTap(_ sender: Any) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .photoLibrary // Or .camera as you require
imagePicker.allowsEditing = true
self.present(imagePicker, animated: true, completion: nil)
}
//MARK:-imagePickerControllerDelegate
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let image1 = info[UIImagePickerController.InfoKey.editedImage] as? UIImage
self.ImagePhoto.image = image1
self.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
print("Cancel")
self.dismiss(animated: true, completion: nil)
}
Upvotes: 4
Reputation: 11
Something I found that helped me was making sure the delegate was set as public
rather than private
.
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
Upvotes: 1
Reputation: 13537
As per my experience, it is an issue of ARC.
If we define instance as locally then ARC will remove its reference automatically once methods scope end. If you define globally then it is kept in memory until the view controller is not deinitialized.
Define
UIImagePickerController
instance globally.
I have created once the common class of NSObject
and delegates
method of UIImagePickerController
is not called.
After 5 hours of brainstorming, Finally, get the solution. It seems like an issue related to memory deallocation during the captured image from the camera.
public typealias CameraBlock = (UIImage?, Bool) -> Void
class HSCameraSingleton: NSObject {
var pickerController = UIImagePickerController()
private var completionBlock: CameraBlock!
var presentationController: UIViewController?
public init(presentationController: UIViewController) {
super.init()
self.presentationController = presentationController
}
public func present(completionBlock: @escaping CameraBlock) {
guard UIImagePickerController.isSourceTypeAvailable(.camera) else {
return
}
self.pickerController = UIImagePickerController()
self.pickerController.delegate = self
self.pickerController.allowsEditing = true
self.pickerController.sourceType = .camera
self.completionBlock = completionBlock
self.presentationController?.present(self.pickerController, animated: true, completion: nil)
}
}
extension HSCameraSingleton: UIImagePickerControllerDelegate,UINavigationControllerDelegate {
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.completionBlock?(nil,false)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.originalImage] as? UIImage else {
self.completionBlock?(nil,false)
return
}
self.completionBlock?(image,true)
pickerController.dismiss(animated:true, completion: nil)
}
}
class AuthViewController: UIViewController{
lazy var overlay = HSCameraSingleton(presentationController:self)
@IBAction func actionLoginTap(_ sender: UIControl) {
overlay.present { (image, status) in
print(image,status)
}
}
}
Upvotes: 7
Reputation: 7691
I had to copy the method names straight from the delegate. For some reason the auto-complete has the method headers wrong.
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
//save image
//display image
}
self.dismiss(animated: true, completion: nil)
}
public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.dismiss(animated: true, completion: nil)
}
UPDATE SWIFT 5:
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
//save image
//display image
}
self.dismiss(animated: true, completion: nil)
}
Upvotes: 23
Reputation: 25294
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
print("\(info)")
if let image = info[.originalImage] as? UIImage {
imageView?.image = image
dismiss(animated: true, completion: nil)
}
}
}
let imagePickerController = UIImagePickerController()
imagePickerController.allowsEditing = false
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = self
present(imagePickerController, animated: true, completion: nil)
Do not forget to add the solution code here (look above)
import UIKit
class ViewController: UIViewController {
private weak var imageView: UIImageView?
override func viewDidLoad() {
super.viewDidLoad()
let stackView = UIStackView(frame: .zero)
stackView.axis = .vertical
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFit
imageView.translatesAutoresizingMaskIntoConstraints = false
stackView.addArrangedSubview(imageView)
imageView.widthAnchor.constraint(equalToConstant: 200).isActive = true
imageView.heightAnchor.constraint(equalToConstant: 200).isActive = true
self.imageView = imageView
let button = UIButton(frame: .zero)
button.setTitle("Button", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.addTarget(self, action: #selector(showImages), for: .touchUpInside)
stackView.addArrangedSubview(button)
}
@IBAction func showImages(_ sender: AnyObject) {
let imagePickerController = UIImagePickerController()
imagePickerController.allowsEditing = false
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = self
present(imagePickerController, animated: true, completion: nil)
}
}
Upvotes: 16
Reputation: 74
I voted this one up because I was missing the UINavgationControllerDelegate
declaration and this comment helped.
imagePickerController
wasn't being called.
Upvotes: 1
Reputation: 2142
I also faced this issue and solved it by using below solution. Set picker's delegate after present completion.
controller.present(picker, animated: true, completion: {
self.picker.delegate = self
})
Hope this will work for you!!
Upvotes: 8
Reputation: 121
Yo have to make sure that UIImagePickerController was not released before delegate called.
I created an ImagePicker class to handle all the UIImagePickerController stuff.
I created similar class, but
func onButtonDidTap(sender: UIButton) {
.....
let picker = VLImagePickerController()
picker.show(fromSender: sender, handler: { (image: UIImage?) -> (Void) in
if (image != nil) {
self.setImage(image!)
}
})
....
}
did not work for me. 'picker' was released before 'handler' could be called.
I created permanent reference, and it worked:
let picker = VLImagePickerController()
func onButtonDidTap(sender: UIButton) {
.....
//let picker = VLImagePickerController()
picker.show(fromSender: sender, handler: { (image: UIImage?) -> (Void) in
if (image != nil) {
self.setImage(image!)
}
})
....
}
Upvotes: 5
Reputation: 880
I found that the delegate code had to be within an active UIViewController.
I originally tried to have my code in a separate file, as as NSObject with the correct delegate protocols declared, like this:
class PhotoPicker: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
But that never called the delegate methods.
Taking the exact same code and placing it within the UIViewController I was calling it from made it work.
It looks like the best solution is to create a pop-up type view, and have its ViewController keep the code.
Upvotes: 5
Reputation: 9266
This code works, (although, it redisplays over and over because it displays the picker in viewWillAppear, this is just to keep code small). I would look at what is different from this. It could have to do with your top view controller? Why not just display the picker from a view controller rather than go to application's top view controller? Also, once you get the delegate callback, you need to dismiss the view controller.
import UIKit
import MobileCoreServices
class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
let imagePicker = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
imagePicker.mediaTypes = [kUTTypeImage as String]
imagePicker.delegate = self
}
override func viewDidAppear(_ animated: Bool) { // keeps reopening, do not this in your code.
present(imagePicker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
imagePicker.dismiss(animated: true, completion: nil)
}
}
Upvotes: 2