Swift - Passing an UIImage to an UIImageView via delegation

2.9k Views Asked by At

I'm out of ideas and I have no idea how to solve this. Any help will be appreciated.

I've got the following situation:

  • MainViewController which points to a CollectionView embedded in a NavController
  • CollectionView points to a DetailViewController
  • DetailViewController shows the full picture chosen in the CollectionView and has a button which passes this image to the MainViewController. Protocol and delegate are used for this
  • This image should now be shown in an UIImageView in the MainViewController

The delegation works. I have an UIImage in my MainViewController. BUT: the UIImageView is nil. So when I set the chosen Image as the imageView.image it crashes. The MainViewController is initialized and I also see my default Picture. See below for the code, and the console-output.

MainViewController

    class MainViewController: UIViewController, UserChosePhoto {

    @IBOutlet weak var googleImageView: UIImageView!
    var usedImage: UIImage? = nil

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        googleImageView.image = usedImage
        println("viewDidLoad: \(googleImageView)")
    }

    func userHasChosen(image: UIImage) {
        usedImage = image
        println("imageView: \(googleImageView)")
        println("delegation: \(image)")
    }
}

DetailViewController

 protocol UserChosePhoto {
    func userHasChosen(image: UIImage)
}

class GoogleDetailViewController: UIViewController {

    @IBOutlet weak var bigImageView: UIImageView!
    var image: UIImage? = nil
    var delegate: UserChosePhoto? = nil

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        bigImageView.image = image
        let barButtonItem = UIBarButtonItem(title: "Use", style: UIBarButtonItemStyle.Plain, target: self, action: "tapped")
        self.navigationItem.rightBarButtonItem = barButtonItem
        self.delegate = MainViewController()
    }

    func tapped() {
        if (delegate != nil) {
            self.delegate!.userHasChosen(bigImageView.image!)
        }
        self.dismissViewControllerAnimated(true, completion: nil)
    }

}

Console Output: the first viewDidLoad-log comes from the app launch. found 10 pictures - a log from an API-call. imageView - this is the imageView in question. delegation: here i have an UIImage

viewDidLoad: <UIImageView: 0x7f94825cabf0; frame = (180 159; 240 282); autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x7f94825caeb0>> - (null)
found 10 pictures
imageView: nil
delegation: <UIImage: 0x7f94848344a0>, {213, 290}

Any tips would be cool - I'm really lost right now.

Thanks!

2

There are 2 best solutions below

2
On BEST ANSWER

This is likely the issue:

self.delegate = MainViewController()

Here you are setting the DetailViewController's delegate to a new instance of MainViewController (hence the parenthesis). You need to set the delegate to its parent (which is also of type MainViewController)

When you call self.dismissViewControllerAnimated(true, completion: nil) you are now going back to your original MainViewController but the function userHasChosen was never called on this View Controller, and thus it crashes.

So, essentially what you need to do is make sure the delegate of your DetailViewController is set to its parent, which you might be able to get by just doing myDetailViewController.parent (Sometimes this doesn't work for me, but I'm not in XCode to test at the moment) or you can pass a reference of the parent to your child view controller and set that as its delegate.

EDIT: I made a few assumptions on how your code and view controller's are set up that may not be entirely accurate. If your control flow indeed goes MainVC -> CollectionView -> DetailVC, then you may not be able to just get its "parent". Instead you'll have to do the second part of my suggestion which is just pass a reference of your MainVC to your DetailVC and then you can use it in that ViewController instead of creating a new MainViewController.

And because it may be unclear: When I refer to the DetailViewController's parent, I mean the MainViewController in which it came from, not the CollectionView (even though the button from there is what took you to the DetailVC)

1
On

You're never actually setting the image property for the image view. The only time you do it is in viewDidLoad at which point it probably doesn't exist and you don't do it again. The action of setting it is a one time thing and does not bind the data, so you have to update it yourself any time you update the property. This should do it:

func userHasChosen(image: UIImage) {
        usedImage = image
        googleImageView.image = usedImage
        println("imageView: \(googleImageView)")
        println("delegation: \(image)")
    }

Another way (and arguably more neat) to do this would be to implement a didSet hook for your image property.

var usedImage: UIImage? = nil
{
    didSet
    {
        googleImageView.image = usedImage
    }
}