Mehrdad Faraji
Mehrdad Faraji

Reputation: 3894

use Alamofire to show image in collectionView in ios

I use Alamofire library for showing image in cell in the collectionView but my problem is when I scrolling up && down , my CollectionView showing wrong image in cell

and this is my snippet code for set cell data

override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CardViewCell

    let pos = indexPath.item % 5

    var paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.lineSpacing = 2.5
    paragraphStyle.alignment = .Right
    paragraphStyle.lineBreakMode = .ByTruncatingTail

    if let text = currentCollection.titles?[indexPath.item] {
        var attrString = NSMutableAttributedString(string: text)
        attrString.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range:NSMakeRange(0, attrString.length))
        cell.title.attributedText = attrString
    } else {
        cell.title.text = currentCollection.titles?[indexPath.item]
    }
    if let catId = currentCollection.catIds?[indexPath.item] {
        cell.iconImage.image = UIImage(named: icons[catId-1])
    }

    cell.title.font = UIFont(name: "B Yekan", size: 14)
    cell.title.preferredMaxLayoutWidth = cell.frame.size.width - 48

    cell.iconImage.image = cell.iconImage.image!.imageWithRenderingMode(.AlwaysTemplate)
    cell.iconImage.tintColor = UIColor.whiteColor()

    let imageUrl = currentCollection.urlImages![indexPath.item]

    if let image = currentCollection.imageCache.objectForKey(imageUrl) as? UIImage {
        cell.backImage.image = image
    } else {
        cell.backImage.image = nil

        cell.request = Alamofire.request(.GET, imageUrl).validate(contentType: ["image/*"]).responseImage() {
            (request, _, image, error) in
            if error == nil && image != nil {
                self.currentCollection.imageCache.setObject(image!, forKey: request.URLString)

                cell.backImage.image = image
            }
        }
    }


    cell.layer.shadowPath = UIBezierPath(roundedRect: cell.bounds, cornerRadius: cell.layer.cornerRadius).CGPath

    if indexPath.item == currentCollection.titles!.count-3 {
        currentCollection.page++
        appendTitlesInPage(currentCollection.page)
    }



    return cell
}

can help me where is the wrong? please!

Upvotes: 1

Views: 1701

Answers (3)

bpapa
bpapa

Reputation: 21497

Look at the "LazyTableImages" sample app that Apple provides. It's with a UITableView, but it would be essentially the same code as with a UICollectionView, and you don't need a third party library to do it.

IconDownloader *iconDownloader = (self.imageDownloadsInProgress)[indexPath];
    if (iconDownloader == nil) 
    {
        iconDownloader = [[IconDownloader alloc] init];
        iconDownloader.appRecord = appRecord;
        [iconDownloader setCompletionHandler:^{

            UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];

            // Display the newly loaded image
            cell.imageView.image = appRecord.appIcon;

            // Remove the IconDownloader from the in progress list.
            // This will result in it being deallocated.
            [self.imageDownloadsInProgress removeObjectForKey:indexPath];

        }];
        (self.imageDownloadsInProgress)[indexPath] = iconDownloader;
        [iconDownloader startDownload]; 

The relevant part to you is in the completion handler, where you're getting the cell using cellForRowAtIndexPath. This (and the corresponding collection view method) ensure you're getting the right cell, and if it's offscreen it returns nil and everything degrades gracefully.

Upvotes: 0

Guillermo Chiacchio
Guillermo Chiacchio

Reputation: 319

EDIT: Now I have developed a small CocoaPods library to handle this problem seamlessly. I suggest you to use this one, I have it running in a couple of projects myself without any issues.

https://github.com/gchiacchio/AlamoImage


As @johnpatrickmorgan said, the problem is: when you scroll the cell is reused, and by the time the request for the image is responded, it's no longer valid (image & cell don't match).

As the response time is determined by networking conditions, you only can wait for the response, but what you CAN DO is to CANCEL the request in progress at the moment you know the cell will be reused. To accomplish that you can put a cell.request?.cancel() BEFORE setting the new image, as follows:

let imageUrl = currentCollection.urlImages![indexPath.item]

//Cancel the request in progress (to a wrong image) if any.
cell.request?.cancel()

if let image = currentCollection.imageCache.objectForKey(imageUrl) as? UIImage {
    cell.backImage.image = image
} else {
    cell.backImage.image = nil

    cell.request = Alamofire.request(.GET, imageUrl).validate(contentType: ["image/*"]).responseImage() {
        (request, _, image, error) in
        if error == nil && image != nil {
            self.currentCollection.imageCache.setObject(image!, forKey: request.URLString)

            cell.backImage.image = image
        }
    }
}

Upvotes: 1

johnpatrickmorgan
johnpatrickmorgan

Reputation: 2372

The image request takes time - during that time the request keeps a reference to the cell, and sets the cell's backImage.image once the request is complete. Unfortunately, by that time, the cell may have been scrolled off-screen and may be being re-used at another indexPath, where the image is incorrect. Instead, you should use the table view's cellForItemAtIndexPath method to get the correct cell (if the cell is no longer visible, this will return nil which will prevent your error).

Upvotes: 1

Related Questions