tableView单元有时显示错误的图像?

mos*_*321 1 uitableview uiimageview ios swift

我有一个tableView在单元格中显示图像。在大多数情况下,将显示正确的图像,但是偶尔会显示错误的图像(通常是非常快速地向下滚动tableView)。我异步下载图像并将其存储在缓存中。找不到导致问题的其他原因吗??下面是代码:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    // try to reuse cell
    let cell:CustomCell = tableView.dequeueReusableCellWithIdentifier("DealCell") as CustomCell

    // get the venue image
    let currentVenueImage = deals[indexPath.row].venueImageID
    let unwrappedVenueImage = currentVenueImage
    var venueImage = self.venueImageCache[unwrappedVenueImage]
    let venueImageUrl = NSURL(string: "http://notrealsite.com/restaurants/\(unwrappedVenueImage)/photo")

    // reset reused cell image to placeholder
    cell.venueImage.image = UIImage(named: "placeholder venue")

    // async image
    if venueImage == nil {

        let request: NSURLRequest = NSURLRequest(URL: venueImageUrl!)

        NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!,data: NSData!,error: NSError!) -> Void in
            if error == nil {

                venueImage = UIImage(data: data)

                self.venueImageCache[unwrappedVenueImage] = venueImage
                dispatch_async(dispatch_get_main_queue(), {

                    // fade image in
                    cell.venueImage.alpha = 0
                    cell.venueImage.image = venueImage
                    cell.venueImage.fadeIn()

                })
            }
            else {

            }
        })
    }

    else{
        cell.venueImage.image = venueImage
    }

    return cell

}
Run Code Online (Sandbox Code Playgroud)

Oli*_*ver 5

我认为问题出在sendAsynchronousRequest. 如果您的滚动速度比这更快,当您重用一个单元格时,您最终可能会用旧的completionHandler替换“错误”的单元格(因为它现在显示不同的条目)。您需要在完成处理程序中检查它仍然是您要显示的图像。


小智 5

迅捷3

我使用NSCache为图像加载器编写了自己的轻量级实现。 没有细胞图像闪烁!

ImageCacheLoader.swift

typealias ImageCacheLoaderCompletionHandler = ((UIImage) -> ())

class ImageCacheLoader {

    var task: URLSessionDownloadTask!
    var session: URLSession!
    var cache: NSCache<NSString, UIImage>!

    init() {
        session = URLSession.shared
        task = URLSessionDownloadTask()
        self.cache = NSCache()
    }

    func obtainImageWithPath(imagePath: String, completionHandler: @escaping ImageCacheLoaderCompletionHandler) {
        if let image = self.cache.object(forKey: imagePath as NSString) {
            DispatchQueue.main.async {
                completionHandler(image)
            }
        } else {
            /* You need placeholder image in your assets, 
               if you want to display a placeholder to user */
            let placeholder = #imageLiteral(resourceName: "placeholder")
            DispatchQueue.main.async {
                completionHandler(placeholder)
            }
            let url: URL! = URL(string: imagePath)
            task = session.downloadTask(with: url, completionHandler: { (location, response, error) in
                if let data = try? Data(contentsOf: url) {
                    let img: UIImage! = UIImage(data: data)
                    self.cache.setObject(img, forKey: imagePath as NSString)
                    DispatchQueue.main.async {
                        completionHandler(img)
                    }
                }
            })
            task.resume()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用范例

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "Identifier")

    cell.title = "Cool title"

    imageLoader.obtainImageWithPath(imagePath: viewModel.image) { (image) in
        // Before assigning the image, check whether the current cell is visible for ensuring that it's right cell
        if let updateCell = tableView.cellForRow(at: indexPath) {
            updateCell.imageView.image = image
        }
    }    
    return cell
}
Run Code Online (Sandbox Code Playgroud)