Reputation: 1108
I have a standalone class to fetch an image from the camera/photo library using UIImagePickerController.
I cannot seem to figure out why the didFinishPickingMediaWithInfo or the imagePickerControllerDidCancel methods are not called..? I realise this question has been asked many times but I cannot figure it out... I know that all the method names are correct.
I have properly set all of the following in the info.plist:
import UIKit
class ImagePickerService: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
fileprivate var completion: ((UIImage) -> ())
fileprivate var imagePicker: UIImagePickerController
init(completion: @escaping ((UIImage) -> ())) {
self.completion = completion
self.imagePicker = UIImagePickerController()
super.init()
imagePicker.delegate = self
}
func show(from viewController: UIViewController) {
showAlert(from: viewController)
}
fileprivate func showAlert(from viewController: UIViewController) {
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "Take a photo", style: .default) { _ in
self.openCamera(from: viewController)
})
alert.addAction(UIAlertAction(title: "Choose from library", style: .default) { _ in
self.openGallery(from: viewController)
})
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
viewController.present(alert, animated: true, completion: nil)
}
// MARK: Image Picker
fileprivate func openCamera(from viewController: UIViewController) {
guard UIImagePickerController.isSourceTypeAvailable(.camera) else {
viewController.showOKAlert(title: "Error!", message: "No camera available")
return
}
showImagePicker(withSource: .camera, from: viewController)
}
fileprivate func openGallery(from viewController: UIViewController) {
showImagePicker(withSource: .photoLibrary, from: viewController)
}
fileprivate func showImagePicker(withSource source: UIImagePickerControllerSourceType, from viewController: UIViewController) {
imagePicker.sourceType = source
viewController.present(imagePicker, animated: true, completion: nil)
}
}
extension ImagePickerService {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
guard let image = info[UIImagePickerControllerEditedImage] as? UIImage else {
return
}
completion(image)
picker.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
}
EDIT: The problem was that I was not creating a strong reference to the ImagePickerService on instantiation and it was released automatically before the delegate methods could be called.
Upvotes: 0
Views: 1265
Reputation: 2913
Try creating a singleton of the ImagePickerService Class :
static let shared = ImagePickerService()
After creating the singleton the reference to the service object will maintain and it will not be released.
Set the delegate in init() and make a method to show ImagePicker.
func pickMedia(from: UIViewController?,didPickMedia: ((UIImage) -> ())?){
//self.openCamera()
//or
//self.openGallery()
}
From that singleton you can present it as :
ImagePickerService.shared.pickMedia(from: self) { (image) in
}
Upvotes: 0
Reputation: 2689
The problem is in the following code snippet:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
guard let image = info[UIImagePickerControllerEditedImage] as? UIImage else {
return
}
completion(image)
picker.dismiss(animated: true, completion: nil)
}
You are expecting that there is always an edited image, which may not be the case for all photos in your photo library. Try using the UIImagePickerControllerOriginalImage
key instead when info[UIImagePickerControllerEditedImage]
returns nil
.
If these methods aren't called in the first place, you need to make sure you have a strong reference to the ImagePickerService
instance.
For example, this code works:
class ViewController: UIViewController {
var picker: ImagePickerService?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { [weak self] in
guard let `self` = self else { return }
self.picker = ImagePickerService() { (image) in
print(image)
}
self.picker?.show(from: self)
}
}
}
Upvotes: 3