Passing an UIImage to an UIImageView via delegation

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 var googleImageView: UIImageView!
    var usedImage: UIImage? = nil

    override func viewDidLoad() {
        super.viewDidLoad()
        /
        println("viewDidLoad: \(googleImageView)")
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        /
    }

    func userHasChosen(image: UIImage) {
        usedImage = image
        googleImageView.image = usedImage
        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()
        /
        bigImageView.image = image
        let barButtonItem = UIBarButtonItem(title: "Use", style: UIBarButtonItemStyle.Plain, target: self, action: "tapped")
        self.navigationItem.rightBarButtonItem = barButtonItem
        self.delegate = MainViewController()
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        /
    }

    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: 0x7f863056f980; frame = (180 236; 240 128); autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x7f863056f710>> - (null)
found 10 pictures matching adam
imageView: nil
delegation: <UIImage: 0x7f86305ce6f0>, {300, 300}


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

Thanks!

All apps need a "model" that represents information and behavior. It's a good idea to go ahead and formalize that model, ie the M in MVC. As your app grows in complexity your model can grow as well, and you won't have to change any of your ViewControllers or other interfaces that are clients of the model. Below is an example of creating an app model and referencing it in various ViewControllers. I personally think it would be best if you could set the model on your ViewControllers, instead of referencing it via AppDelegate, but I've found that doing that is tedious and have settled on this pattern for now.

class AppModel {

    var selectedImage : UIImage?


    init() {}

}

class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {

    var window: UIWindow?
    private static var model : AppModel?

    class func appModel() -> AppModel {
        return model!
    }

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        AppDelegate.model = AppModel()
        //...
    }

}

class MainViewController: UIViewController {

    @IBOutlet var googleImageView: UIImageView!
    var model: AppModel? = nil


    override func viewDidLoad() {
        super.viewDidLoad()
        model = AppDelegate.appModel()
        //...
    }

    // ...when a user chooses an image...
    model.selectedImage = // Retrieve from where your storing your images. I woud put them in AppModel as well.

}

class GoogleDetailViewController: UIViewController {

    @IBOutlet weak var bigImageView: UIImageView!
    var model: AppModel? = nil


    override func viewDidLoad() {
        super.viewDidLoad()
        model = AppDelegate.appModel()
        /
    }

    // ...When you need to use the selected image...
    bigImageView.image = model.selectedImage

}
Accepted Answer

The problem is that when you do this


        self.delegate = MainViewController()


you're creating a brand new instance of MainViewController that is not the one that's actually on the screen. You need to pass a reference to the instance that's actually in your view controller hierarchy. Typically this is done in prepareForSegue, something like


let destVC = segue.destinationViewController as FooViewController
destVC.delegate = self


where FooViewController is the class that defines the delegate property. It sounds like your situation is a bit more complex as you have some other VCs in the middle there, but the key point is the same - you need to pass the actual reference to the specific instance of MainViewController on down the chain. You can't just create a new one.

That solved it, thanks very much!

Passing an UIImage to an UIImageView via delegation
 
 
Q