views:

159

answers:

3

I've got a custom UITableViewCell class whose model object performs an asynchronous download of an image which is to be displayed in the cell. I know I've got the outlets connected properly in IB for WidgetTVC, I know that image data is being properly returned from my server, and I've alloc/init'd the widget.logo UIImage too. Why is the image always blank then in my tableViewCell? Thanks in advance.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    Widget *theWidget = [widgetArray objectAtIndex:indexPath.row];

    static NSString *CellIdentifier = @"WidgetCell";
    WidgetTVC *cell = (WidgetTVC*)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if(cell == nil)
    {
        [[NSBundle mainBundle] loadNibNamed:@"WidgetTVC" owner:self options:nil];
        cell = self.widgetTVC;
        self.widgetTVC = nil;
    }

    [cell configureWithWidget:theWidget];

    return cell;
}

In my WidgetTVC class, I have the following method:

- (void)configureWithWidget:(Widget*)aWidget {
    self.widget = aWidget;
    self.nameLbl.text = aWidget.name;
    [self.logoIvw setImage:aWidget.logo]; // logoIvw is the image view for the logo
}

Finally- I've got the callback method that sets the logo UIImage property on the Widget model object asynchronously (simplified):

 (void)didReceiveImage:(ASIHTTPRequest *)request {
    // I pass a ref to the Widget's logo UIImage in the userInfo dict
    UIImage *anImage = (UIImage*)[request.userInfo objectForKey:@"image"];      
    UIImage *newImage = [UIImage imageWithData:[request responseData]];
    anImage = newImage;
 }
A: 

In didReceiveImage:, you're only modifying the local pointer anImage. You need to set the image property of your UIImageView in order to update what gets displayed. Instead of stashing a reference to your widget's UIImage, pass a reference to the UIImageView, and in didReceiveImage: do something like

UIImageView *imageView = (UIImageView*)[request.userInfo objectForKey:@"imageView"]; 
UIImage *newImage = [UIImage imageWithData:[request responseData]];
imageView.image = newImage;
bosmacs
But it still doesn't show an image even when I give the app plenty of time to ensure that the logo property was set *before* "configureWithWidget" is called as the cell is created (which calls setImage on the cell's image view).
Steve N
What if I store a (UIImage**) in userInfo? Would that work? How do I need to modify the code to handle this (I'm a little fuzzy on all the pointer logic)?
Steve N
I'm not sure I understand why you want to access the image directly; wouldn't it be simpler keep a pointer to the image view and set it directly? Have you tried setting the image of your image view directly (with a local image) in `cellForRowAtIndexPath:` to make sure it's working?
bosmacs
I store the logo image as part of the widget model object, and those exist whether or not there is a view to display them. So when I need to display them in some context, I pull the logo image into an imageView... Wouldn't it be odd to have a UIImageView as a property of a model object?
Steve N
I guess I'm not understanding something... I'm not suggesting you add a `UIImageView` to your model object; I'm suggesting you pass it in `userInfo` in your request, which I assumed was context data for your callback. The point is I think you want to find a way to access the imageView and set the image directly rather than attempt to set it via pointer-to-pointer tricks.
bosmacs
A: 

Perhaps the best solution would be to have your model object have the image as a property and the display object subscribe to the image property's changes through KVO and update itself whenever the image property changes.

Ed Marty
A: 

OK- there were a couple things wrong here. Thanks to bosmacs and Ed Marty as both of your comments were used to get to the solution.

First, I added a method to the Widget object to get the logo asynchronously:

- (void)asyncImageLoad {
    ...
    // logo is a UIImage
    [AsyncImageFetch fetchImage:&logo fromURL:url];
}

And my own AsyncImageFetch class looks like this:

+ (void)fetchImage:(UIImage**)anImagePtr fromURL:(NSURL*)aUrl {     
    ASIHTTPRequest *imageRequest = [ASIHTTPRequest requestWithURL:aUrl];    
    imageRequest.userInfo = [NSDictionary dictionaryWithObject:[NSValue valueWithPointer:anImagePtr] forKey:@"imagePtr"];
    imageRequest.delegate = self;
    [imageRequest setDidFinishSelector:@selector(didReceiveImage:)];
    [imageRequest setDidFailSelector:@selector(didNotReceiveImage:)];
    [imageRequest startAsynchronous];
}

+ (void)didReceiveImage:(ASIHTTPRequest *)request {
    UIImage **anImagePtr = [(NSValue*)[request.userInfo objectForKey:@"imagePtr"] pointerValue];    
    UIImage *newImage = [[UIImage imageWithData:[request responseData]] retain];
    *anImagePtr = newImage;
}

Finally, per Ed, I added this to the configureWithWidget method that helps set up my WidgetTVC:

[aCoupon addObserver:self forKeyPath:@"logo" options:0 context:nil];

And when a change is observed, I update the imageView and call [self setNeedsDisplay]. Works like a charm. Any way I can give you both points?

Steve N