Lazy image loading in UITableView iPhone SDK
What I have created for the project of my works as follows; Extend UITableViewCell class by category in "UITableViewCell + Async.h" (See some examples if you don't know what category is in Obj C)
@interface UITableViewCell (Async)
-(void)loadAsyncImage:(NSString*)url withIndex:(NSInteger)index inWidth:(NSInteger)width inHeight:(NSInteger)height;
-(void)loadAsyncBackground:(NSMutableArray*)parameters;
@end
And then in the implementation file "UITableViewCell + Async.m"
#import "UITableViewCell+Async.h"
@implementation UITableViewCell (Async)
-(void)loadAsyncImage:(NSString*)url
withIndex:(NSInteger)index
inWidth:(NSInteger)width
inHeight:(NSInteger)height {
NSMutableArray* parameters = [NSMutableArray arrayWithCapacity:2];
[parameters addObject:url];
[parameters addObject:[NSNumber numberWithInteger:index]];
[parameters addObject:[NSNumber numberWithInteger:width]];
[parameters addObject:[NSNumber numberWithInteger:height]];
self.imageView.tag = index;
[self performSelectorInBackground:@selector(loadAsyncBackground:) withObject:parameters];
}
-(void)loadAsyncBackground:(NSMutableArray*)parameters {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString* url = [parameters objectAtIndex:0];
NSInteger index = [[parameters objectAtIndex:1] integerValue];
NSInteger width = [[parameters objectAtIndex:2] integerValue];
NSInteger height = [[parameters objectAtIndex:3] integerValue];
UIImage* image = [Utils getImageResized:url inSize:CGSizeMake(width, height)];
if (self.tag==index) {
self.imageView.image = image;
[self setNeedsLayout];
}
[pool release];
}
@end
It basically adds the functionality of the UITableViewCell to load the image into a new background thread, resize the image, and bind it to the image view. A tag check is added to see if the cell is still waiting as it might be reused, and another thread for the image might load another image for the reused cell ...
The function in the above code with a signature;
+(UIImage*)getImageResized:(NSString*)url inSize:(CGSize)size;
checks the local image cache, downloads the image from the internet if not to the cache, stores it in the local cache, resizes the image to a given size and returns the image, all this is done when the sync (blocking) method is called, Since this is already a background thread, no harm does not block it for this operation. When the method returns an image, it is set in the cell's image view if it still has the same tag (not reused for any other row)
In the cellForRowAtIndexPath method, you can add a new category, and you must do so;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"......."];
if (cell == nil) {
................
}
................
[cell loadAsyncImage:deal.logo withIndex:indexPath.row inWidth:40 inHeight:40];
................
return cell;
}
a source to share
I used ASINetworkQueue for lazy loading.
In the .h file
#import "ASINetworkQueue.h"
and define a variable
//array contains url address of images and a variable of ASINetworkQueue;
NSMutableArray *arrImg;
ASINetworkQueue *queue;
In the .m file
static int rowNo = 0;
- (void)downloadImages{
if(queue == nil){
queue = [[[ASINetworkQueue alloc] init] autorelease];
}
for(NSString* urlString in arrImg)
{
NSURL *url = [NSURL URLWithString:urlString];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request setDidFinishSelector:@selector(requestDone:)];
[request setDidFailSelector:@selector(requestWentWrong:)];
[queue addOperation:request];
}
[queue go];
}
- (void)requestDone:(ASIHTTPRequest*)req{
for (int i = 0; i < [arrImg count]; i++) {
NSMutableString *d = [[NSMutableString alloc] init];
d = [arrImg objectAtIndex:i];
//comparing the url to get correct row no of cell
if([[req.url absoluteString] isEqualToString:d]){
rowNo = i;
break;
}
}
//creating a cell with rowNo
NSIndexPath *index = [NSIndexPath indexPathForRow:rowNo inSection:0];
UITableViewCell *cell = (UITableViewCell *)[roomTable cellForRowAtIndexPath:index];
UIImageView *v = (UIImageView *)[cell.contentView viewWithTag:10];
v.image = [UIImage imageWithData:[req responseData]];
[cell release];
cell = nil;
[v release];
v = nil;
}
- (void)requestWentWrong:(ASIHTTPRequest*)req{
for (int i = 0; i < [arrImg count]; i++) {
NSMutableString *d = [[NSMutableString alloc] init];
d = [arrImg objectAtIndex:i];
//comparing the url to get correct row no of cell
if([[req.url absoluteString] isEqualToString:d]){
rowNo = i;
break;
}
}
NSLog(@"No data for cell : %d",rowNo);
NSLog(@"Request returned an error %@",[req error]);
}
a source to share