MHLazyTableImages – Efficiently Load Images for Large Tables
The iOS Developer Library has a sample project named LazyTableImages that demonstrates how to load images asynchronously for a table with many rows.
The app first downloads the list of top paid apps from iTunes (this is an XML file) and shows the list of apps in a UITableView. Then it downloads the icons for the apps in the background and displays them as they become available. Until the icon is loaded, a placeholder image is visible instead.
Only the icons for the visible rows are downloaded; when you scroll the table to make new rows visible, it downloads the icons for these new rows (but only when you are done scrolling).
For a recent client project, I needed to do something similar on several different screens. I took the code from this sample project and rewrote it so it was more generic and could be reused among my different view controllers. Today I decided to refactor this some more and make the code available as an open source component. That component is named MHLazyTableImages and you can download it at github.
To demonstrate how to use this class, I modified Apple’s original LazyTableImages sample. The logic for downloading the images is now handled by the MHLazyTableImages and MHImageCache classes. The table view controller only has to create an instance of MHLazyTableImages and connect its data model and table view to it.
To put an image into a table view cell, you simply call -addLazyImageForCell:withIndexPath:. This will first see if the image is already present in the cache and will download it if not.
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath { UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:...]; if (cell == nil) cell = [[[UITableViewCell alloc] initWithStyle:...]; cell.textLabel.text = ...; [lazyImages addLazyImageForCell:cell withIndexPath:indexPath]; } |
Of course, you need to tell MHLazyTableImages about the URL for the image. That happens in a delegate callback method:
- (NSURL*)lazyImageURLForIndexPath:(NSIndexPath*)indexPath { AppRecord* appRecord = [self.entries objectAtIndex:indexPath.row]; return [NSURL URLWithString:appRecord.imageURLString]; } |
I used a delegate — rather than telling MHLazyTableImages directly what the URL should be for the cell — in order to accommodate scrolling. While scrolling, we don’t want the images to load yet. We will defer downloading until the user stops scrolling. What that happens, -lazyImageURLForIndexPath: is automatically called for the newly visible rows.
There are a few more things that need to happen to make it all work, but that is the gist of it.
Just for fun, I also replaced the original networking code with ASIHTTPRequest. I was already using this class in MHImageCache, and it allowed me to use blocks instead of delegates. That means the demo project will work only on OS 4 and up.
Check out the demo project (RootViewController in particular) to see how everything works in detail.


