1

So I have an application that reads records from a database and basically fills out a UITableView with the information from the DB. Some of the information includes an image, which it brings from an online server I have. Everything works fine, but the program is a little slow and scrolling is a little laggy/ jumpy, as its bringing all the images at once, instead of bringing them as I scroll. Is there a way to change it so that as I scroll it brings the next few visible records?

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {

    static NSString *CellIdentifier = @"VersionCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
                                      reuseIdentifier:CellIdentifier];
    }

    STSneakerInfo *info = [_versionInfo objectAtIndex:indexPath.row];

    cell.textLabel.font = [UIFont boldSystemFontOfSize:14.1];
    [[cell textLabel] setNumberOfLines:2];

    cell.textLabel.text = [[_uniqueBrand stringByAppendingString:@" "] stringByAppendingString: info.version];

    cell.detailTextLabel.textColor = [UIColor grayColor];
    cell.detailTextLabel.font = [UIFont boldSystemFontOfSize:14.1];
    cell.detailTextLabel.text = info.initialReleaseDate;

    NSString *brandVersion = [[_uniqueBrand stringByAppendingString:@" "] stringByAppendingString:info.version];
    NSString *underscoredBrandVersion = [brandVersion stringByReplacingOccurrencesOfString:@" " withString:@"_"];
    NSString *underscoredBrandName = [_uniqueBrand stringByReplacingOccurrencesOfString:@" " withString:@"_"];

    NSData *imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: ([[[[[@"http://www.abc/img/" stringByAppendingString:underscoredBrandName] stringByAppendingString:@"/"] stringByAppendingString:underscoredBrandVersion] stringByAppendingString:@"/"] stringByAppendingString:@"default.jpg"])]];
    cell.imageView.image = [UIImage imageWithData: imageData];

    return cell;
}
Black Frog
  • 11,595
  • 1
  • 35
  • 66
Tal Zamirly
  • 117
  • 1
  • 11
  • you can use [SDWebImage](https://github.com/rs/SDWebImage) for async image downloading. please show some code for `cellForRowAtIndexPath:`. – ChintaN -Maddy- Ramani Oct 14 '14 at 04:13
  • @Chinttu-RoxeN-Ramani I have added some code above – Tal Zamirly Oct 14 '14 at 04:20
  • checkout my and WenchenHuang answer. but i prefer SDWebImage is good library. it will handle cache. – ChintaN -Maddy- Ramani Oct 14 '14 at 04:26
  • @Chinttu-RoxeN-Ramani - Its working very fast now which is good, but I am having one small problem. In this app I have a few different tableViews, and selective different table cell move you to different tableViews. So lets say my categories are fruit, vegetables and cheese, and I click on cheese, it will load all the types of cheese efficiently with the right pictures, but if I click on a type of cheese, lets say yellow cheese, it will bring be to a table with all the pictures being the first picture. Any idea what might cause this? Does this make sense? – Tal Zamirly Oct 14 '14 at 04:50
  • you mean you have to show images for cheese category in fullview with animation. – ChintaN -Maddy- Ramani Oct 14 '14 at 04:53
  • The code above is in my second tableView (types of food so cheese, fruit, vegetables etc). Once I click one of them it loads the third tableView (lets say cheese) and brings up all the different types of cheese. But for some reason, all the records have the exact same picture (all the first picture), which doesnt happen if I load it like my code above. – Tal Zamirly Oct 14 '14 at 04:56
  • are you reloading table when click on specific row (say cheese.) and changing your url as per your data? – ChintaN -Maddy- Ramani Oct 14 '14 at 05:08
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/63002/discussion-between-zambot-and-chinttu-roxen-ramani). – Tal Zamirly Oct 14 '14 at 05:16

4 Answers4

2

The UITableView class never loads cells until they're about to appear onscreen. I suggest you to use GCD to download image background.When finished,notice UI to change.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       //Download images
        dispatch_async(dispatch_get_main_queue(), ^{
            //Notice UI to change
        });
});
Leo
  • 24,596
  • 11
  • 71
  • 92
2

you can use (https://github.com/rs/SDWebImage) to download image async. its easy and fast.

i prefered this library because it will handle your cache. just write below code

[cell.imageView sd_setImageWithURL: [NSURL URLWithString: ([[[[[@"http://www.abc/img/" stringByAppendingString:underscoredBrandName] stringByAppendingString:@"/"] stringByAppendingString:underscoredBrandVersion] stringByAppendingString:@"/"] stringByAppendingString:@"default.jpg"])]];

you can also download image in background thread as per wenchenHuang answer above. using below code.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString: ([[[[[@"http://www.abc/img/" stringByAppendingString:underscoredBrandName] stringByAppendingString:@"/"] stringByAppendingString:underscoredBrandVersion] stringByAppendingString:@"/"] stringByAppendingString:@"default.jpg"])];
        if (data)
        {
            UIImage *img = [UIImage imageWithData:data];
            dispatch_async(dispatch_get_main_queue(), ^{
                if (img)
                    cell.imageView.image = img;
            });
        }

    });

Maybe this will help you.

ChintaN -Maddy- Ramani
  • 5,156
  • 1
  • 27
  • 48
  • SDWeb is admirable, but DLImageLoader is really hard to beat. it's incredibly solid http://stackoverflow.com/a/19115912/294884 – Fattie Oct 14 '14 at 07:27
1

Here's a full, real-world example with DLImageLoader

https://github.com/AndreyLunevich/DLImageLoader-iOS/tree/master/DLImageLoader

DLImageLoader is incredibly well-written, maintained constantly, is super-lightweight, and it can properly handle skimming.

It's difficult to beat and is used in many apps with vast numbers of users.

It is really an amazingly well maintained library - and on top of that the new Swift version is the pinnacle of excellence in Swift programming, it's a model for how to do it.

PFObject *aFacebookUser = [self.fbFriends objectAtIndex:thisRow];
NSString *facebookImageURL = [NSString stringWithFormat:
    @"http://graph.facebook.com/%@/picture?type=large",
    [aFacebookUser objectForKey:@"id"] ];

__weak UIImageView *loadMe = self.cellImage;
[DLImageLoader loadImageFromURL:facebookImageURL
   completed:^(NSError *error, NSData *imgData)
    {
    if ( loadMe == nil ) return;

    if (error == nil)
        {
        UIImage *image = [UIImage imageWithData:imgData];
        image = [image ourImageScaler];
        loadMe.image = image;
        }
    else
        {
        // an error when loading the image from the net
        }
    }];

another real world example,

-(UICollectionViewCell *)collectionView:(UICollectionView *)cv
         cellForItemAtIndexPath:(NSIndexPath *)indexPath
    {
    NSInteger thisRow = indexPath.row;
    BooksCell *cell;
    cell = [cv dequeueReusableCellWithReuseIdentifier:
              @"CellBooksNormal" forIndexPath:indexPath];

    cell.layer.shouldRasterize = YES;
    cell.layer.rasterizationScale = [UIScreen mainScreen].scale;

    // set text items...
    cell.title = @"blah;

    // set image items using DLImageLoader...

    __weak UIBookView *loadMe = cell.anImage;
    [DLImageLoader loadImageFromURL:imUrl
       completed:^(NSError *error, NSData *imgData)
        {
        [loadMe use:[UIImage imageWithData:imgData]];
        }];

    return cell;
    }
Fattie
  • 27,874
  • 70
  • 431
  • 719
0

Reason for your sluggish scroll is that you are downloading image on the main thread.

NSData *imageData = [[NSData alloc] initWithContentsOfURL:

That piece of code which is running on main thread, will make a network call and start downloading contents from server. And the execution halts there until its completed. Which means you wont be be able to scroll down anymore till image is loaded.

There are many workarounds for this. However the logic is same for all. Download image in a separate thread and load it once its completed.

If you are using AFNetworking in your project you can use setImageWithURL: on your UIImageView object. You need to include UIImageView+AFNetworking.h. It has a inbuilt caching mechanism and it will cache the images downloaded for that session.

GoodSp33d
  • 6,252
  • 4
  • 35
  • 67