SDW
SDW

Reputation: 1910

UITableView is laggy when scrolling up

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:

enter image description here

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

Answers (7)

Pradeep Mittal
Pradeep Mittal

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

Ankush
Ankush

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

Dávid Kaszás
Dávid Kaszás

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

Amar
Amar

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

Arthur Shinkevich
Arthur Shinkevich

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

gleb.kudr
gleb.kudr

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

Michael Dautermann
Michael Dautermann

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

Related Questions