Вопрос

UICollectionView is used to display images. It works fine.

Issue is when I scroll UICollectionView, some of the cells which were visible are now non-visible and when I scroll back so that they are visible again, I see that they are not properly displayed.

So what happens is that imageA is at index 0 and imageB is at index 1. Now after scrolling and coming back original scroll position when those cells are loaded again, their position is either switched or both the cell have same images.

Initially :

enter image description here

After reloading cells :

enter image description here

Code :

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

    DLog(@"url at index %d : %@", indexPath.row, ((MyObject *)[self.list.array objectAtIndex:indexPath.row]).imageUrl);

    MyObject *object = (MyObject *)[self.list.array objectAtIndex:indexPath.row];

    // Get image in separate thread
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // Get image to display
        UIImage * tempImage = object.image;

        if (tempImage)
        {
            // Run code to set image to UIImageView in main thread only.
            dispatch_async(dispatch_get_main_queue(), ^{

                cell.imageView.image = tempImage;
            });
        }
        else
        {
            // Download failed.
            DLog(@"Downloaded image is nil.");
        }
    });

    return cell;
}

I log the value of image url and it is logging correct value.

Это было полезно?

Решение

I also used that trick, but the threads and the indexes between threads make a mess so I searched and I found a great library on GitHub.

You just need to #import <SDWebImage/UIImageView+WebCache.h> to your project, and you can define also the placeholder when image is being downloaded with just this code:

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

    DLog(@"url at index %d : %@", indexPath.row, ((MyObject *)[self.list.array objectAtIndex:indexPath.row]).imageUrl);

    MyObject *object = (MyObject *)[self.list.array objectAtIndex:indexPath.row];

    [cell.imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
                   placeholderImage:[UIImage imageNamed:@"placeholder.png"]];

    return cell;
}

It also cache downloaded images and gives you great performance.

Hope it will help you!

Другие советы

Why you are wondering that after some time they switch to their positions? You call

cell.imageView.image = tempImage;

asynchronously. That means that execution doesn't wait while image changes and displays dequeued cell with old image.

There are a couple of issues here, but mainly the problem you are seeing is because the cell reference is no longer pointing to the same Cell. As a learning exercise, keeping your code (or at least the general layout of your code), you can overcome the issue by getting a reference to the cell you intended the item to be applied to via the indexPath and if it's still being displayed, assign the image.

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

    DLog(@"url at index %d : %@", indexPath.item, ((MyObject *)[self.list.array  objectAtIndex:indexPath.item]).imageUrl);

    MyObject *object = (MyObject *)[self.list.array objectAtIndex:indexPath.item];

    // get a weak reference to collectionViewController (assume your code is in a UICollectionViewCcontroller)

    __weak MyCollectionViewController *weakSelf = self; 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

     // Get image to display
      UIImage * tempImage = object.image;

     // check if the cell is still visible
     if ([weakSelf.collectionView.indexPathsForVisibleItems containsObject:indexPath]) {

          if (tempImage)
          {
            // Run code to set image to UIImageView in main thread only.
            dispatch_async(dispatch_get_main_queue(), ^{
               // we get a reference to the cell via it's indexPath
                MyCell *cell = (MyCell *)[weakSelf.collectionView cellForItemAtIndexPath:indexPath];
                cell.imageView.image = tempImage;
            });
         }
         else
        {
          // Download failed.
          DLog(@"Downloaded image is nil.");
        }
     }
    });

  return cell;
}

It would certainly be better, as Erid suggested, that you use something like SDWebImage and store the image url in your MyObject Class and set the cell.imageView.image using SDWebimage's setImageWithURL (personally i would write the image retrieval and image reference in the object class).

Oh, one minor point, you should use indexPath.section and indexPath.item for UICollectionViews and indexPath.row for UITableView.s

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top