views:

656

answers:

5

Hi. I've been drawing custom table cells (using the samples from apple as a base) and have now come to having to do a cell which displays an image from a URL - each cell would have a different image (based on some data it has) but all the cells are the same and so the same reuse id.

What's the correct structure for doing this? Obviously I need to load the image in a new thread. I've got the following function so far sitting in the cells view class which is run in its own thread:


- (void)loadImage
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    self.img = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString: [myProduct objectForKey:@"ImagePath"]]]];
    [self setNeedsDisplay];
    [pool release];
}

When I call this from the drawRect function itself (which is bad) then it "works", but this obviously gets called every time anything happens (selection etc.). If I put it in the init function of the cells uiview, then it only gets called for the first 8 cells and then they're reused. Other variations ended up making the image not get 'reset' when the cell is reused and so the same 8 images repeat down the table (although the other text updates).

I'm not worried about caching for the moment, but are there any samples of how to do this, or can anyone point me in the right direction? Thanks.

A: 

Well, when you dequeue the cell you can force the image loading there. You don't have to do it within the init... typically you do:

cell = dequeueCell...
if (!cell) {
   //create one
}

return cell;

What you can do is before the return cell, you can force the image loading if it's the type of cell that you want. This way, whenever it displays, you can grab it from your cache (if it exists) and if it doesn't, go to the web for it.

Malaxeur
How would I handle the thread bits to this? I can call the thread in cellforrowatindexpath which goes and fetches the image, but at that point the cell will have drawn itself (minus the image) already, and then I'd need to call setNeedsDisplay on the cells view again?
Tom
+2  A: 

The solution is going to have to be more sophisticated, because by default UITableView recycles cells (when one scrolls off the top it is moved to the bottom and reconfigured with new data). Therefore it's possible that you start downloading an image but the cell's content is changed before the download is complete.

In cellForRowAtIndexPath:, you should get the image from a cache (your myProduct object, an NSArray, whatever). If it's not there, you should check a flag to see if it is already being loaded. If it isn't already being loaded, then you set that flag and detach a new thread (you should look into NSOperation, it will queue up the work and protect you from launching too many threads at once).

In your thread, you should download the image and then use performSelectorOnMainThread:target:waitUntilDone: to call a method on the main thread with the image. That method can update the cache, set the isLoading flag to NO, and update the cell. It's important to do it this way, because Cocoa requires that all your UI update code be on the main thread.

I hope that's a useful outline of what to do.

benzado
Thanks, this helped. I took a look at the threaded flickr tableview code from the stanford series which helped as well.I've got it working currently by doing cell.image = [cachedImg:url] which then fires off the NSOperation and then does a reloadData on the tableview when complete, however I'd quite like to use the custom drawing as with the rest of the cell -how would I go about this? I'd need to pass the image to the cell class, which passes it to its view class and then calls setNeedsDisplay? How can I reference the original cell in the method that's called by performSelectorOnMainThread?
Tom
I find a really good approach to this is to use NSOperations in combination with NSNotifications - you can have a cell subscribe to a notification for image loading which the complete load of the image fires off. You can attach something like the URL of the image to make sure the notification you get back relates to the image you are waiting for, and unsubscribe in prepareForReuse so only visible cells get changed when the notification is sent out.
Kendall Helmstetter Gelner
A: 

have a look at appropriate slides/video/code example from stanford lectures cs193p ... iphone application programming ...

stefanB
A: 

If you have not seen explicit documentation from Apple to the effect that UIImage is thread-safe I don't think you should be doing that. Instead get your image as NSData and turn it into UIImage in your main thread. The basic rule is that all UIKit work MUST be done in your main thread or behaviour is undefined.

Adam Eberbach
A: 

This should answer your query.LazyTableImages

black2842