UICollectionView reloads wrong images on scroll

后端 未结 3 2049
天命终不由人
天命终不由人 2021-02-14 11:46

In the code below, I download image URLs from a text file, store it in a NSMutableArray, then download the images from from those URLS in CellforItemAtIndexPath. However, when I

3条回答
  •  暖寄归人
    2021-02-14 12:14

    I noticed a bunch of downvotes without comments and after reading my answer again I guess the original accepted answer (below the line) could have been better.

    There are a few things going on with these kinds of problems. The UITableView only creates just enough UITableViewCells to display every cell in view plus what's going to scroll into view. The other cells are going to get re-used after they scroll out of view.

    This means when you're scrolling fast the cell gets set up multiple times to fetch an image async and then display it because it's pretty slow fetching the image. After the image loads it will show itself on the cell that originally called to fetch it, but that cell might be re-used already.

    Nowadays I have a bufferedImage: UIImage? property on whatever data class I use as data for the UITableViewCells. This will initially always be nil so I fetch the image async and when the dispatch_async returns it will store it there so the next time I need to show this cell it's ready, and if it's still the same cell I will also display it in the cell.

    So the right way to do it is:

    • Start configuring the cell with your data object
    • Store the data object in your cell so it can be referred later
    • Check if the data object already has an image set in bufferedImage and if so display it and that's it
    • If there's no image yet, reset the current image to nothing or a placeholder and start downloading the image in the background
    • When the async call returns store the image in the bufferedImage property
    • Check if the object that is stored in the current cell is still the same object you fetched the image for (this is very important), if not don't do anything
    • If you're still in the same cell display the image

    So there's one object you pass along in your async call so you can store the fetched image. There's another object that's in the current cell that you can compare to. Often the cell already got reused before you got the image, so those objects are different by the time your image is downloaded.


    Scrolling and rotating CollectionViews always has been a bit problematic. I always have the same problem, images that appear in the wrong cells. There is some kind of optimization going on for the collectionview that affects images but not labels. To me it's a bug but it still isn't solved after three iOS releases.

    You need to implement the following:

    https://developer.apple.com/library/ios/documentation/uikit/reference/UICollectionViewLayout_class/Reference/Reference.html#//apple_ref/occ/instm/UICollectionViewLayout/shouldInvalidateLayoutForBoundsChange:

    This should always return YES basically to always refresh it. If this solves your problem then you could look into not executing the whole redraw every time as it's bad for performance. I personally spent a lot of time in counting if a screen had passed or line had passed and so on but it became stuttery instead so I ended up just returning YES. YMMV.

提交回复
热议问题