Loc Dai Le
Loc Dai Le

Reputation: 1707

UICollectionView repeating cell

I have an UICollectionView where there is an image in each cell. These image are loaded in by an URL. But when an cell is scroll out of view it show another cell's image for a second before loading is own again. I don't know why this is happening.

Here is my code:

My UIImageView extension to load from url:

extension UIImageView {
    public func imageFromUrl(urlString: String) {

        Alamofire.request(.GET, urlString).responseJSON{
            response in

            if let tilbudimage = UIImage(data: response.data!){
                self.image = tilbudimage

            }

        }
    }
}

UICollectionView:

func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

        print(DealArr.count)

        return DealArr.count
    }

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("DealCell", forIndexPath: indexPath) as! DealCell

        let deal = self.DealArr[indexPath.row]

        cell.backgroundColor = UIColor.whiteColor()

        cell.DealImage.imageFromUrl(deal["image"].string!)
        cell.DealLabel.text = deal["title"].string! + ", " + String(deal["price"].int!) + "kr."
        cell.DealDescription.text = deal["description"].string!
        cellarr.append(cell)



        return cell




    }


    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
        return CGSizeMake(collectionView.frame.width, 450)
    }


    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets {
        return UIEdgeInsetsMake(0, 0, 0, 0)
    }

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
        return 0.0
    }

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
        return 1.0
    }

Upvotes: 3

Views: 2581

Answers (2)

Phani Bob
Phani Bob

Reputation: 805

You can use this one from custom UICollectionViewCell class.

override func prepareForReuse() {
    self.imageView.image = nil // or placeholder image

    super.prepareForReuse()
}

Upvotes: 4

esthepiking
esthepiking

Reputation: 1647

You aren't clearing out the image when the cell is displayed. Cells get reused after they are removed from the screen, so before displaying you need to make sure you clear out any data that shouldn't be carried over. Usually this is handled by setting any data to the new values. In the case of your image, instead of immediately setting the new image, it dispatches an asynchronous task to fetch the image, then it shows that image in the cell. So the image will get reassigned, but it will wait until it receives a response from the server with the new image before clearing out the old image.

To fix this, you should set the image equal to nil before loading the new image.

cell.DealImage.image = nil
cell.DealImage.imageFromUrl(deal["image"].string!)

Or your extension could also start by clearing the image before loading the new one.

extension UIImageView { 
    public func imageFromUrl(urlString: String) {
        image = nil
        Alamofire...

You can also improve the efficiency of the loading by cancelling any pending requests when reusing a cell. There's a good tutorial that includes this on raywenderlich if you're interested: http://www.raywenderlich.com/85080/beginning-alamofire-tutorial

You can also improve performance by caching the images after they've been downloaded. For that you can use an instance of NSCache.

let imageCache = NSCache()

extension UIImageView { 
    public func imageFromUrl(urlString: String) {
        if let image = imageCache.objectForKey(urlString) as? UIImage {
            self.image = image
        }
        else {
            Alamofire.request(.GET, urlString).responseJSON{
                response in
                if let tilbudimage = UIImage(data: response.data!){
                    self.image = tilbudimage
                    imageCache.setObject(tilbudimage, forKey: urlString)
                }
            }
        }
    }
}

Upvotes: 6

Related Questions