jacobsieradzki
jacobsieradzki

Reputation: 1108

UIImagePickerControllerDelegate not called

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

Answers (2)

Agent Smith
Agent Smith

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

silicon_valley
silicon_valley

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

Related Questions