0

Im using the following code to populate a collection view cells with images.

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"Cell";

    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];

    UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
    recipeImageView.image = nil;
    if ([imageArray count] >0){

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
            NSData *data0 = [NSData dataWithContentsOfURL: [NSURL URLWithString:[imageArray objectAtIndex:indexPath.row]]];
            UIImage *image = [UIImage imageWithData: data0];

            dispatch_sync(dispatch_get_main_queue(), ^(void) {
                recipeImageView.image = image;
            });
        });

    }

    [spinnerShow stopAnimating];


    return cell;
}

The problem is that, when Im scrolling the images are flickering and are flashing. Why is that so? How can I make those images to be stable without flickering?

Jagat Dave
  • 1,643
  • 3
  • 23
  • 30
  • Are seeing it on simulator? If yes then make sure you are viewing simulator at full scale by pressing command+1. – Bharat Modi Jun 07 '16 at 04:26
  • do you override any scrollview methods in the viewcontroller containing the collection view? – Fonix Jun 07 '16 at 04:27
  • 1
    as cell is reuse and you download every time so it flick .after download put it in document directory and before download check if available in document direcoty then use it from there, – Prashant Tukadiya Jun 07 '16 at 04:33
  • Did you try it? : http://stackoverflow.com/questions/37614651/collectionview-images-are-flashing-while-scrolling/37646390#37646390 – Cuong Nguyen Jun 07 '16 at 04:39
  • you can try : http://stackoverflow.com/a/37560713/1850983 – Payal Maniyar Jun 07 '16 at 04:44
  • what if you comment out this line `recipeImageView.image = nil;` just to experiment – SanitLee Jun 07 '16 at 04:45
  • Im using real device not simulator –  Jun 07 '16 at 04:50
  • No im not overriding any scrollview methods –  Jun 07 '16 at 04:50
  • I have tried all of those links –  Jun 07 '16 at 04:50
  • sure..i will comment it out and try again –  Jun 07 '16 at 04:51
  • Why did you use this code `dispatch_sync(dispatch_get_main_queue(), ^(void) {`? Did you fully understand how it works? Please read the [doc](https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html) you need use `dispatch_async` for update your imageview – iSashok Jun 07 '16 at 05:07

2 Answers2

0

as per my knowledge, you are fetching image but not caching it that's why when your UICollectionViewCell gets reload, you get UIImageView's fresh instance so and this thing goes on and on in your code..

in this case, i recommend you to use SDWebImage OR AFNetworking Frameworks. because these frameworks does all the tricky stuff for you with the simple line of code (SDWebImage Framework),

NSURL* url = [NSURL URLWithString:str];
[yourImageView setBackgroundImageWithURL:url forState:UIControlStateNormal placeholderImage:kPlaceholder];
qtmfld
  • 2,916
  • 2
  • 21
  • 36
Vatsal Shukla
  • 1,274
  • 12
  • 25
  • Is there any ways to solve it without using third party libraries –  Jun 07 '16 at 04:51
  • @KaruppuMGR , of course , you have to store your downloaded image to cache. just check this Accepted Answer, it has detailed Answer. http://stackoverflow.com/questions/11511548/best-way-to-cache-images-on-ios-app – Vatsal Shukla Jun 07 '16 at 04:58
0

Just a short overview, So you get your answer

UICollectionView is highly optimized, and thus only keep On-screen visible rows in memory. Now, All rows Cells are cached in Pool and are reused and not regenerated. Whenever, user scrolls the UICollectionView, it adds the just-hidden rows in Pool and reuses them for next to be visible rows.

So, now, coming to your answer

When you scroll your CollectionView, collectionView datasource method gets called again for every indexPath, coming in visible range and your image gets downloaded again

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

SOLUTION

Instantiate a instance NSMutableDictionary, outside of method.

Now in your code

@implementation ClassName{
  NSMutableDictionary *cachedImage;
}

-(void)viewDidLoad(){
  [super viewDidLoad];
  cachedImage = [NSMutableDictionary new];
}

/*OLD CODE*/
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
    recipeImageView.image = nil;
 
    if ([imageArray count] >0){

       //IF image is already downloaded, simply use it and don't download it.
       if(cachedImage[[imageArray objectAtIndex:indexPath.row]] != nil){
           recipeImageView.image = cachedImage[[imageArray objectAtIndex:indexPath.row]];
       }
       else{
           dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
               NSData *data0 = [NSData dataWithContentsOfURL: [NSURL URLWithString:[imageArray objectAtIndex:indexPath.row]]];
               UIImage *image = [UIImage imageWithData: data0];

               dispatch_sync(dispatch_get_main_queue(), ^(void) {
                   recipeImageView.image = image;
                   //****SAVE YOUR DOWNLOADED IMAGE 
                   cachedImage[[imageArray objectAtIndex:indexPath.row]] = image; //****SAVE YOUR DOWNLOADED IMAGE 
               });
           });
       }
}        
/*OLD CODE*/
qtmfld
  • 2,916
  • 2
  • 21
  • 36
gunjot singh
  • 2,578
  • 20
  • 28