AlamofireImage af_setImageWithURL in a UITableViewCell without placeholder image
Asked Answered
S

2

11

I'm using AlamofireImage to set an image on a UIImageView in a UITableViewCell like so:

cell.imageView.af_setImageWithURL(url)

The image doesn't show after downloading. It will show the image the second time when it's loaded from the memory cache.

It seems that using a placeholder image makes all the difference.

This works and prints the address of the (downloaded) image:

cell.imageView.af_setImageWithURL(URL, placeholderImage: UIImage(named: "placeholder"), filter: nil, imageTransition: .None, completion: { (response) -> Void in
                    print("image: \(cell.imageView.image)")
                })

This doesn't work and prints "image: nil"

cell.imageView.af_setImageWithURL(URL, placeholderImage: nil, filter: nil, imageTransition: .None, completion: { (response) -> Void in
                    print("image: \(self.image)")
                })

It also works when setting the cells imageView to an empty image before doing af_setImageWithURL:

cell.imageView.image = UIImage()

Is this a bug in UITableViewCell, AlamofireImage or am I doing something wrong?

Sabbat answered 2/12, 2015 at 3:8 Comment(1)
It looks like cell.imageView sizes itself accordingly, depending on whether there is an image or not. If there is no image, it shrinks its frame to take no space. So, when you don't use a placeholder, it thinks there is no image, so shrinks itself. Then, when the image comes in asynchronously, the frame is already shrunk, so nowhere to put the image.Sanctify
I
7

This question is couple of years old, but maybe this answer can be useful to someone else with a similar problem, like me before to find the right solution.

Since the images are loaded asynchronously, if we didn't provide a fixed height for the UIIMageView we have to force a cell update when the download is finished. This because cell updating (i.e. AutoLayout Constraints recalculation) is done automatically only after cellForRowAt method, that is called when a cell is displayed for the first time, or when the table is scrolled to show other cells. In both cases probably the images are not yet downloaded by the af_setImage() method, so nothing but the placeholder will be displayed since their sizes are unknown for the moment.

To force a cell update we need to use beginUpdates() and endUpdates() methods, putting them inside the completion handler of .af_setImage(). This way, every time the downloading is completed, the cell will be updated.

But, to avoid a loop, before to call beginUpdates()/endUpdates() we have to check if we have already update the cell before, because by calling these methods, the cellForRowAt method is called again and consequently the af_setImage() and its completion closure with beginUpdates()/endUpdates() inside it).

This means that we have to update the cell only when the download is just finished, and not when the image is already cashed (because, if it is cashed, it means that we have already updated the cell). This can be accomplished by checking the response of the completion handler: if it is not nil, the image was just downoladed, if it is nil, the image was cashed.

As a side benefit the cell height will be automagically adjusted (remember to put tableView.estimatedRowHeight = 400 and tableView.rowHeight = UITableViewAutomaticDimension in your viewDidLoad() method)

Finally, here it is the code:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // Dequeue your cell
        let cell = self.tableView.dequeueReusableCell(withIdentifier: "YourCustomCellIdentifier") as! YourCustomTableViewCell

        // get picture url from the data array
        let pictureUrl = self.yourCellsData[indexPath.row].pictureUrl

        // async download
        cell.pictureView.af_setImage(
                withURL:  URL(string: pictureUrl)!,
                placeholderImage: UIImage(named: "YourPlaceholder.png"),
                filter: nil,
                imageTransition: UIImageView.ImageTransition.crossDissolve(0.5),
                runImageTransitionIfCached: false) {
                    // Completion closure
                    response in
                        // Check if the image isn't already cached
                        if response.response != nil {
                            // Force the cell update
                            self.tableView.beginUpdates()
                            self.tableView.endUpdates()
                        }
                }

        return cell
        }

That's all folks! ;-)

Inconsolable answered 9/6, 2017 at 9:58 Comment(4)
Hi Guido, this code works very well! Unfortunately it has a major issue: when scrolling, the table is redesigned and scrolls up and down, it is a very bed experience for users. Is this happening only to me?Rickety
Hi Francesco, I don't remember having had a similar problem, can you post a sample project that show this issue?Inconsolable
Hi Guido, I have fixed my issue by evaluating the right cell height and by creating new cells with fixed values. I do not have any more the broken code, I'm sorry. I'll try to reproduce, if I'll find a way to pragmatically have the issue I'll post here again. Thank you!Rickety
This really helped me. Thanks!!Firenew
E
1

You can print the error by adding 2 line of code so that you will get more Idea about the issue.

cell.imageView.af_setImageWithURL(URL, placeholderImage: UIImage(named: "placeholder"), filter: nil, imageTransition: .None, completion: { (response) -> Void in
                    print("image: \(cell.imageView.image)")
                    print(response.result.value) //# UIImage
                    print(response.result.error) //# NSError

                })

In my case there was an issue with Image URL. It was downloading the image when I open it in browser.

Electricity answered 18/3, 2016 at 13:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.