7

I'm pretty new to Objective-C so hopefully this all makes sense. I've downloaded images from a server and have displayed them in an image view in the collectionView cellForItemAtIndexPath: method. The issue I'm facing is that the images don't appear to be caching. It looks like each time a cell is being reused, the associated image from the server is being re-downloaded.

In my viewDidLoad method I'm creating a NSMutableDictionary:

imageDictionary = [[NSMutableDictionary alloc]initWithCapacity:50.0];

From reading the documentation and looking at answers to similar questions I thought that this, plus the following code would be enough. I've been at this for a couple of days and I know there is something I'm missing or a concept I'm not grasping.

#pragma mark - UICollectionView Data Source
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section;{
    NSLog(@"Begin retrieving photos");
    return [self.photos count];
}

-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
    return 1;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;{
    CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MY_CELL" forIndexPath:indexPath];
    cell.imageView.image = nil;

    if (cell.imageView.image == nil) {
    dispatch_queue_t downloadQueue = dispatch_queue_create("image downloader", NULL);
    dispatch_async(downloadQueue, ^{
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[self.photos objectAtIndex:indexPath.row] objectForKey:@"fullimage"]]];
        UIImage *image = [UIImage imageWithData:data];
        [imageDictionary setObject:image forKey:@"Image"];

        dispatch_async(dispatch_get_main_queue(), ^{
            cell.imageView.image = [imageDictionary objectForKey:@"Image"];
            [cell setNeedsDisplay];
        });
    });
    }
    return cell;
}

Any help would be greatly appreciated. Thanks in advance.

BrianS
  • 241
  • 2
  • 11
  • 2
    It looks like you are redownloading the image each time. The collectionview doesn't cache images for you. You'll need to create your own dictionary for caching. Look into NSCache. – yuf Dec 13 '12 at 03:48

1 Answers1

15

@yuf - thanks again for the direction. NSCache seems to be getting me the results I was after. Here is the code that is working properly. If anyone has a similar issue, you can compare the following to my original question.

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;{
    CollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MY_CELL" forIndexPath:indexPath];

    NSString *imageName = [[self.photos objectAtIndex:indexPath.row] objectForKey:@"fullimage"];
    UIImage *image = [imageCache objectForKey:imageName];

    if(image){

        cell.imageView.image = image;
    }

    else{

    cell.imageView.image = nil;

    dispatch_queue_t downloadQueue = dispatch_queue_create("image downloader", NULL);
    dispatch_async(downloadQueue, ^{

        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[self.photos objectAtIndex:indexPath.row] objectForKey:@"fullimage"]]];
        UIImage *image = [UIImage imageWithData:data];

        dispatch_async(dispatch_get_main_queue(), ^{

            cell.imageView.image = image;

        });

        [imageCache setObject:image forKey:imageName];
    });
    }

    return cell;
}
BrianS
  • 241
  • 2
  • 11
  • 5
    This is not totally correct because if cells are being reused correctly it is possible by the time the image is loaded and the cell updated, that instance of the cell is being used to represent another record in your model. Better to update the cache and then call collection view reloadItemsAtIndexPaths: and get cellForItemAtIndexPath to set the image from the cache (if it is visible). – Ants Apr 11 '13 at 02:52
  • Ah, makes sense. In fact, I did run into that issue (only once, but I see what you mean). Thanks! – BrianS Apr 12 '13 at 03:35
  • @Ants, if you can add the code, many people will be benefited (including me). Thanks. – Satyam Jul 16 '13 at 16:14
  • @Satyam the code above is almost right. All that needs to be changed is to check the image cache first and if the image for this item does not exist then call the code to download it. Once downloaded then call reloadItemsAtIndexPaths: and pass the indexPath of this row to reload it. – Ants Jul 16 '13 at 23:04
  • @Ants, strangely enough even after using `reloadItemsAtIndexPaths` I still getting the issue you have mentioned :( – zanona Oct 13 '13 at 14:44