Swift 3:tableView复制来自Firebase的加载图片

我正试图显示一个用户列表与图像。 这是代码

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! UserListTableViewCell let message = messages[indexPath.row] cell.message = message return cell } 

在UserListTableViewCell文件中,我正在下载图像(也使用caching)并将其显示在表格中。 但是我看到一些行有重复/重复的图像。 在重新加载重复消失,但它的垂直滚动回来。

有时候是这样的 : 在这里输入图像说明

这是UserListTableViewCell文件:

 import UIKit import Firebase class UserListTableViewCell: UITableViewCell { @IBOutlet weak var username: UILabel! @IBOutlet weak var textMessage: UILabel! @IBOutlet weak var timeLabel: UILabel! @IBOutlet weak var userImage: UIImageView! var message: Message? { didSet { if let id = message?.chatPartnerID() { let database = Database.database().reference() let ref = database.child("Users").child(id) ref.observeSingleEvent(of: .value, with: { (snapshot) in if let dictionary = snapshot.value as? [String: AnyObject] { self.username.text = dictionary["username"] as? String if let profileImageUrl = dictionary["image"] as? String, profileImageUrl != "" { self.userImage.image = UIImage(named: "blank-user") self.userImage.loadImageFromCacheWithUrlString(urlString: profileImageUrl) self.userImage.layer.cornerRadius = self.userImage.frame.size.width/2 self.userImage.clipsToBounds = true } else { self.userImage.image = UIImage(named: "blank-user") self.userImage.layer.cornerRadius = self.userImage.frame.size.width/2 self.userImage.clipsToBounds = true } } }, withCancel: nil) } textMessage.text = (message?.text)! + " " + (message?.chatPartnerID())! if let seconds = message?.timestamp?.doubleValue { let timestampDate = Date(timeIntervalSince1970: seconds) let dateFormatter = DateFormatter() dateFormatter.dateFormat = "hh:mm a" timeLabel.text = dateFormatter.string(from: timestampDate) } //userImage.image = message?. } } } 

图像下载扩展文件:

 extension UIImageView { func loadImageFromCacheWithUrlString( urlString: String) { if urlString != "" { self.image = nil if let cachedImage = imageCache.object(forKey: urlString as AnyObject) { self.image = cachedImage as? UIImage self.contentMode = .scaleAspectFill return } let url = URL(string : urlString ) URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in if error != nil { print(error!) return } DispatchQueue.main.async(execute: { if let downloadedImage = UIImage(data: data!) { imageCache.setObject(downloadedImage, forKey: urlString as AnyObject) self.image = downloadedImage self.contentMode = .scaleAspectFill } }) }).resume() } } 

}

基本上你使用单元复用,所以每次新的重用以前的单元,所以它是重用以前的单元格数据,所以你需要清除以前的数据。 您应该无图像查看下载新图像或使用占位符图像

 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! UserListTableViewCell . cell.userImage.image = nil let message = messages[indexPath.row] cell.message = message return cell } 

更新: 从苹果文档

如果一个UITableViewCell对象是可重用的 – 也就是说,它有一个重用标识符 – 这个方法在从UITableView方法dequeueReusableCell(withIdentifier :)返回之前被调用。 出于性能原因,您应该只重置与内容无关的单元格的属性,例如,alpha,编辑和select状态。 tableView(_:cellForRowAt :)中的表视图委托应该在重用单元时总是重置所有的内容。 如果单元对象没有关联的重用标识符,则不调用此方法。 如果重写此方法,则必须确保调用超类实现。

所以把你的UserListTableViewCell

 override func prepareForReuse() { super.prepareForReuse() self.userImage.image() = nil // self.userImage.image = nil // above line not working then try this } 

另一个解决scheme

你可以使用UIImage(data: Data(contentsOf: URL(string: urlString)!))从url中获取图像,但占用主线程,所以caching是必须的。 此解决scheme不会显示错误图像,但不好,因为初始化原因延迟。

 class URLImageRetriever { fileprivate static var cache = NSCache<AnyObject,UIImage>() static func getImage(from urlString: String) -> UIImage? { var image = URLImageRetriever.cache.object(forKey: urlString as AnyObject) if image == nil { do { try image = UIImage(data: Data(contentsOf: URL(string: urlString)!)) if let image = image { Designer.cache.setObject(image, forKey: urlString as AnyObject) } } catch { } } return image } } 

起源

你的扩展loadImageFromCacheWithUrlString可能不适合这种情况。

我们来讨论一下这种情况:

  1. 您的ImageView请求呈现url-a图像。 现在有一个URLSession task-a正在运行。
  2. 你的UITableViewCell被重用来呈现另一个数据源,所以你的ImageView a要求呈现url-b图像。 现在有一个URLSession task-b正在运行。
  3. task-b -B从url-b检索图片。 您的ImageView现在完全显示来自url-b图像。
  4. 以后再拍一张照片。 您的ImageView被设置为显示图像forms的url-a 。 让我们感到困惑。

每当你检索图片时,你应该检查你想用你的imageview显示什么。

例如:

 URLImageUtil.loadImageFromCacheWithUrlString(urlString: profileImageUrl, completion: { url, image in if self.message.profileImageUrl == url { self.userImage.image = image } }) class URLImageUtil { static func loadImageFromCacheWithUrlString(urlString: String, completion: @escaping (_ url: URL,_ image: UIImage)->Void) { if let url = URL(string: urlString) { URLSession.shared.dataTask( with: url, completionHandler: {data,response,error in if (data) != nil { if let image = UIImage.init(data: data!) { completion(url, image) } else { print("data not image from url: \(url)") } } else { print("no data from url: \(url)") } }) } else { print("error url: \(urlString)") } } } 

你可以试试这个方法吗?

 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! UserListTableViewCell cell.userImage.image = UIImage(named: "blank-user") let message = messages[indexPath.row] cell.message = message return cell }