Tobias
Tobias

Reputation: 578

Swift Async Imageloader

I would like to implement a tableview with cells that display an image. The images will be loaded asynchroniously. For better scrolling I want the request to be canceled if the cell scrolls out of the view. My code so far works, but I don't know how to detect if the cell is visible or already "scrolled over".

Here's my code:

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("EventsTableCell", forIndexPath: indexPath) as! EventsTableCell
    var elem : Event = data[indexPath.row] as Event

    cell.headlineLabel.text = elem.getName()
    cell.secondLabel.text = elem.getDescription()

    cell.progressView.setProgress(0.0,animated: true)

    if let image = ImageCache.getImage(elem.getId()) {
        cell.coverImage.image = image
        cell.progressView.removeFromSuperview()
    } else {
        cell.coverImage.image = UIImage(named: "loading")
        if(elem.getCover() == nil){
            //NOIMAGE
            cell.progressView.removeFromSuperview()
        }else{
            println("start request for" + elem.getName()!)
            cell.request = Alamofire.request(.GET, elem.getCover()!)
                .progress {
                    (_, totalBytesRead, totalBytesExpectedToRead) in

                    dispatch_async(dispatch_get_main_queue()) {
                        // 6
                        cell.progressView.setProgress(Float(totalBytesRead) / Float(totalBytesExpectedToRead), animated: true)

                        // 7
                        if totalBytesRead == totalBytesExpectedToRead {
                            cell.progressView.removeFromSuperview()
                        }
                    }
                }
                .response { (request, response, data, error) in
                    if error == nil && cell.coverImage.image != nil {
                        ImageCache.addImage(elem.getId(),image: UIImage(data: data!, scale:1)!)
                        cell.coverImage.image = UIImage(data: data!, scale:1)
                    }else{

                    }
            }
        }
    }
    return cell
}

And with the following code I can cancel the request:

cell.request!.cancel()

I also noticed that the progressView isn't displayed sometimes, or maybe will be deleted from the superview to early, maybe someone can help.

Thanks Tobias

Upvotes: 2

Views: 472

Answers (2)

matt
matt

Reputation: 534893

For better scrolling I want the request to be canceled if the cell scrolls out of the view.

Implement tableView:didEndDisplayingCell:forRowAtIndexPath: and, in it, cancel the download for this row. That is exactly what this delegate method is for!

Upvotes: 3

Avt
Avt

Reputation: 17043

Cells are reused by table view for performance reasons. So the same cell could be used a lot of time for different indexPath.

Following line

tableView.dequeueReusableCellWithIdentifier("EventsTableCell", forIndexPath: indexPath) as! EventsTableCell 

creates a new EventsTableCell object only if there is no one to be reused!

So now an progressView issue is clear - you remove it from your cell by calling

cell.progressView.removeFromSuperview()

but you never add it. For example you could add it right after dequeueReusableCellWithIdentifier call and remove later if needed. I also think in your case it will be better not to remove/add it but hide/show instead:

cell.progressView.hidden = false

Quite the same for an image request - you can call

cell.request?.cancel()

just after dequeueReusableCellWithIdentifier

To make your code more obvious you can cancel in prepareForReuse method of EventsTableCell but it will have the same effect.

prepareForReuse

Prepares a reusable cell for reuse by the table view's delegate.

Upvotes: 1

Related Questions