Robo Robok
Robo Robok

Reputation: 22815

Image Picker Controller delegate in separate class doesn't work

I'm new to Xcode and Swift. I'm currently playing around with Start Developing iOS Apps (Swift) tutorial by Apple.

Everything works fine so far, except one thing. The tutorial handles Image Picker delegate inside the main ViewController class, making it conform to UIImagePickerControllerDelegate and UINavigationControllerDelegate protocols. I wanted to try something else and move the delegate to separate class. Here's what I did:

import UIKit
import Foundation

class MealPhotoDelegate: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    var placeholder: UIImageView?

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        dismiss(animated: true, completion: nil)
    }

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        guard let selectedImage = info[UIImagePickerControllerOriginalImage] as? UIImage else {
            fatalError("Expected a dictionary containing an image, but was provided the following: \(info)")
        }

        placeholder!.image = selectedImage

        dismiss(animated: true, completion: nil)
    }
}

And in the ViewController:

@IBAction func selectImageFromPhotoLibrary(_ sender: UITapGestureRecognizer) {
    nameTextField.resignFirstResponder()

    let imagePickerController = UIImagePickerController()
    imagePickerController.sourceType = .photoLibrary

    let imagePickerDelegate = MealPhotoDelegate()
    imagePickerDelegate.placeholder = photoImageView

    imagePickerController.delegate = imagePickerDelegate

    present(imagePickerController, animated: true, completion: nil)
}

Every time the Image Picker appears and when I navigate through the photos, the error is being generated:

2017-07-20 01:49:00.937470+0200 FoodTracker[41600:4236501] API error: (null) returned 0 width, assuming UIViewNoIntrinsicMetric

What am I doing wrong? Is UIViewController good choice of a superclass for a delegate? I feel like maybe not.

Upvotes: 2

Views: 2123

Answers (1)

rmaddy
rmaddy

Reputation: 318934

Your issue is one of memory management. Inside your selectImageFromPhotoLibrary you create a local instance of MealPhotoDelegate. At the end of the method, this instance goes out of scope and gets deallocated because there is no strong reference to it. Therefore, the image picker's delegate becomes nil and it doesn't work.

The solution is to make a strong reference to the instance of MealPhotoDelegate. This is easily done using a property in your view controller class.

Add the following property to your view controller class:

var imagePickerDelegate: MealPhotoDelegate?

Then update the line:

let imagePickerDelegate = MealPhotoDelegate()

with:

imagePickerDelegate = MealPhotoDelegate()

That's it. It will work now.

As a side note, there is no reason why your MealPhotoDelegate should extend UIViewController. Make it extend NSObject instead. And you the fix the new issues by using the proper code.

Here's how it should be:

class MealPhotoDelegate: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    var placeholder: UIImageView?

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        picker.dismiss(animated: true, completion: nil)
    }

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        guard let selectedImage = info[UIImagePickerControllerOriginalImage] as? UIImage else {
            fatalError("Expected a dictionary containing an image, but was provided the following: \(info)")
        }

        if let placeholder = placeholder {
            placeholder.image = selectedImage
        }

        picker.dismiss(animated: true, completion: nil)
    }
}

Upvotes: 2

Related Questions