Reputation: 2527
I have a table view that displays custom cells, the cells contain an image which I am loading from the documents folder. I notice that when I scroll the table, there is some lag, which I am assuming is coming from loading the image from the disk. However, the image is already loaded at the point so i'm a little confused.
Suggestions on optimizing this code, would be appreciated. I have read about lazy loading, but i'm not sure if this applies to me or not.
I did check to make sure the table was reusing the cells.
Edit:
- (void)configureCell:(BeerCell *)cell
atIndexPath:(NSIndexPath *)indexPath
{
Beer *beer = (Beer *) [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.displayBeerName.text = beer.name;
// check to see if there is a cached image already. Use a dictionary.
// make sure this is one of your ivars
__block UIImage *theImage = [self.imagesCache objectForKey: beer.imagePath];
// If the image is not in your cache, you need to retrieve it.
if (!theImage){
// The image doesn't exist, we need to load it from disk, web or render it
// First put a placeholder image in place. Shouldn't be any penalties after the
// first load because it is cached.
cell.beerImage.image = [UIImage imageNamed:@"beer-pic.png"];
// check to see if your image cache dictionary has been created, if not do so now
if (_imagesCache == nil) {
_imagesCache= [[NSMutableDictionary alloc] initWithCapacity:1];
}
// get a weak reference to UITableViewController subclass for use in the block
// we do this to avoid retain cycles
__weak BeerListViewController *weakSelf = self;
// do the heavy lifting on a background queue so the UI looks fast
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^ {
theImage = [UIImage imageWithContentsOfFile:beer.imagePath];
// I added this in because I create the new core data object in this class, and pass
// it to the class where I fill out the information about the beer
if (theImage) {
// Add the image to the cache
[weakSelf.imagesCache setObject:theImage forKey:beer.imagePath];
//[weakSelf.imagesCache addObject:theImage forKey:beer.imagePath];
// Check to see if the cell for the specified index path is still being used
BeerCell *theCell = (BeerCell *)[weakSelf.tableView cellForRowAtIndexPath:indexPath];
// Per the docs. An object representing a cell of the table
// or nil if the cell is not visible or indexPath is out of range.
if (theCell){
// dispatch onto the main queue because we are doing work on the UI
dispatch_async(dispatch_get_main_queue(), ^ {
theCell.beerImage.image = theImage;
[theCell setNeedsLayout];
});
}
}
});
}
else
{
// Image already exists, use it.
cell.beerImage.image = theImage;
}
}
Upvotes: 0
Views: 510
Reputation: 4623
Whenever you load from disk, server, or render images for a tableview you will want to put it on a background queue. It is trivial to do so and will get great performance even on a 3GS.
I use a similar approach for generating thumbnails for tableviews and scroll views from very large images and the performance is very good.
Try this:
- (void)configureCell:(CustomCell *)cell
atIndexPath:(NSIndexPath *)indexPath
{
CoreDateObject *object = (CoreDateObject *)[self.fetchedResultsController objectAtIndexPath:indexPath];
cell.displayName.text = object.name;
// check to see if there is a cached image already. Use a dictionary.
// make sure this is one of your ivars
UIImage *theImage=[self.imagesCache objectForKey: object.imagePath];
// If the image is not in your cache, you need to retrieve it.
if (!theImage){
// The image doesn't exist, we need to load it from disk, web or render it
// First put a placeholder image in place. Shouldn't be any penalties after the
// first load because it is cached.
cell.selectedImage.image=[UIImage imageNamed:@"yourPlaceHolderImage"];
// check to see if your image cache dictionary has been created, if not do so now
if (_imagesCache==nil){
_imagesCache=[NSMutableDictionary alloc] initWithCapacity:1];
}
// get a weak reference to UITableViewController subclass for use in the block
// we do this to avoid retain cycles
__weak YourTableViewControllerSubclass *weakSelf=self;
// do the heavy lifting on a background queue so the UI looks fast
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^{
theImage=[UIImage imageWithContentOfFile:object.imagePath];
// Add the image to the cache
[weakSelf.imagesCache addObject:theImage forKey:object.imagePath];
// Check to see if the cell for the specified index path is still being used
CustomCell *theCell=(CustomCell *)[weakSelf.tableView cellForRowAtIndexPath:indexPath];
// Per the docs. An object representing a cell of the table
// or nil if the cell is not visible or indexPath is out of range.
if (theCell){
// dispatch onto the main queue because we are doing work on the UI
dispatch_async(dispatch_get_main_queue(), ^{
theCell.selectedImage.image=theImage
[theCell setNeedsLayout];
});
}
}else{
// Image already exists, use it.
cell.selectedImage.image=theImage;
}
cell.rating.rate = object.rating.doubleValue;
}
Upvotes: 1
Reputation: 13364
cellForRowAtIndexPath:
is called each time when you scroll the table view ... So it can be hang your scrolling if you write large processing code in this method. So for this make an array of your images if possible then use thumbnail images.... then add those images to cell from array. I'm sure your scrolling will be smooth.
Upvotes: 0
Reputation: 3294
one of these two links will better explain how this is done properly..
https://developer.apple.com/library/ios/#samplecode/LazyTableImages/Introduction/Intro.html (official apple)
https://github.com/rs/SDWebImage (nice library to use)..
Upvotes: 0