Reputation: 1910
Solution: Use SDWebImage: https://github.com/rs/SDWebImage
I have a UITableView which becomes very laggy when during scrolling. I discovered the lag appears when the image that I am using is coming back on the screen.
I am using a custom UITableViewCell. Is this also a reason why it's lagging?
My custom UITableViewCell:
My code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifier = [NSString stringWithFormat:@"Cell%d%d",indexPath.row,indexPath.section];
tableViewCellActiviteiten *cell = (tableViewCellActiviteiten *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"tableViewCellActiviteiten" owner:self options:nil];
cell = (tableViewCellActiviteiten *)[nib objectAtIndex:0];
}
cell.thetitle.text = [self.alletitels objectAtIndex:indexPath.row];
cell.thesubtitle.text = [self.allesubtitels objectAtIndex:indexPath.row];
NSString * imagePath = [self.alleimages objectAtIndex:indexPath.row];
NSURL * imageURL = [NSURL URLWithString:imagePath];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage * image = [UIImage imageWithData:imageData];
cell.image.image = image;
return cell;
}
Content of the arrays:
self.alletitels
contains a string: "Activity Title"
self.allesubtitels
contains a string: "Activity Subtitle"
self.alleimages
contains a url: "http://m2.myhappygames.com//files/pics/0/Paranormal_Shark_Activity_3.jpg"
Can anyone advise what might be the cause of the laggy scrolling?
Upvotes: 14
Views: 5899
Reputation: 593
Along with caching you may also consider loading the images in background using Grand central dispatch. When the cell is loaded put a UIActivityIndicator then replace it with an image in a separate thread.
// get a data provider referencing the relevant file
CGDataProviderRef dataProvider = CGDataProviderCreateWithFilename(filename);
// use the data provider to get a CGImage; release the data provider
CGImageRef image = CGImageCreateWithPNGDataProvider(dataProvider, NULL, NO,
kCGRenderingIntentDefault);
CGDataProviderRelease(dataProvider);
// make a bitmap context of a suitable size to draw to, forcing decode
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
unsigned char *imageBuffer = (unsigned char *)malloc(width*height*4);
CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef imageContext =
CGBitmapContextCreate(imageBuffer, width, height, 8, width*4, colourSpace,
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little);
CGColorSpaceRelease(colourSpace);
// draw the image to the context, release it
CGContextDrawImage(imageContext, CGRectMake(0, 0, width, height), image);
CGImageRelease(image);
// now get an image ref from the context
CGImageRef outputImage = CGBitmapContextCreateImage(imageContext);
// post that off to the main thread, where you might do something like
// [UIImage imageWithCGImage:outputImage]
[self performSelectorOnMainThread:@selector(haveThisImage:)
withObject:[NSValue valueWithPointer:outputImage] waitUntilDone:YES];
// clean up
CGImageRelease(outputImage);
CGContextRelease(imageContext);
free(imageBuffer);
You can also use UIImage+ImmediateLoad.m It decodes images immediately. Besides UIImage -initWithContentsOfFile:, -imageWithCGImage: and CG* are thread-safe after iOS4.
Upvotes: 3
Reputation: 2555
You are making a synchronous network call for each image Thats whay you are getting lag in your app. Try AsyncImageView it will download your images from web Asynchronously . And You will be able to remove the lag from your app. :-)
Upvotes: 2
Reputation: 1977
I had a similar problem, I examined it with Profiler, so here are a few tought:
1) You are using wrong the Reuseable Indentifier, you should always use the same identifier.
instead of this:
NSString *CellIdentifier = [NSString stringWithFormat:@"Cell%d%d",indexPath.row,indexPath.section];
use this:
NSString *CellIdentifier = @"MyIdentifier";
for more read on: how to use UItableViewCell reuseIdentifier
2) loadNibNamed method is really slow ( plus you are loading it each time for every cell), you should get rid of the nib, and place the UI elements from code by overwriteing this method of an UITableViewCell
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier;
3) use Instruments
Upvotes: 2
Reputation: 13222
You are making synchronous network calls to download the images while cell is getting created. These calls block the main thread until the image for cell downloads. Lag time will vary based on the quality of network.
The solution for this is use of technique called "Lazy loading". In this technique image loading will take place on a separate thread, thus unblocking the main thread. Image is downloaded and then applied to the correct container UIImageView
. Here is Apple sample code for lazy loading of images in table view.
You can also use SDWebImage. This will do image caching for you as well.
#import <SDWebImage/UIImageView+WebCache.h>
...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = @"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
}
// Here we use the new provided setImageWithURL: method to load the web image
[cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:@"placeholder.png"]];
return cell;
}
Hope that helps!
Upvotes: 4
Reputation: 801
The problem is that every time tableView: cellForRowAtIndexPath:
method is called your code generates an image. This will be called each time a cell appears on the screen, therefore if you scroll really fast this method will start allocating a lot of images synchronously which will slow down the scroll.
As a fast solution, implement NSCache in your View Controller and store images in it.
UIImage *image = [_imageCache objectForKey:@indexPath.row];
if (image == nil) {
NSString * imagePath = [self.alleimages objectAtIndex:indexPath.row];
NSURL * imageURL = [NSURL URLWithString:imagePath];
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
image = [UIImage imageWithData:imageData];
[_imageCache setObject:image forKey:@indexPath.row];
}
cell.image.image = image;
_imageCahce is an instance variable of view controller that you can implement.
Hope this helps, cheers!
Upvotes: 9
Reputation: 1508
You are trying to load images on the same UI thread where you are scrolling. You should load them to cache in the background and then get from cache when they are ready to show.
Upvotes: 2
Reputation: 89559
These two lines here:
NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage * image = [UIImage imageWithData:imageData];
Are what is dramatically slowing down your table performance.
You're doing synchronous fetching and loading of image data every time you refresh that cell.
It would be smarter to somehow cache (or save locally) the images to be displayed. And if they aren't saved locally or in a cache, only then go fetch those images (and do that asychronously, outside of that "cellForRowAtIndexPath:
" method).
Upvotes: 6