Swift 3:tableView从Firebase复制加载的图像

时间:2017-07-24 15:37:09

标签: ios swift uitableview 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文件中,我正在下载图像(也使用缓存)并在桌面上显示它。但我发现有些行有重复/重复的图像。重新加载后重复消失,但它的背面垂直滚动。

发生这种情况:enter image description here

这是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()
    }


}

}

3 个答案:

答案 0 :(得分:4)

基本上,您使用单元重用,因此每次重新使用以前的单元格时,都会重复使用以前的单元格数据,因此您需要清除以前的数据。你应该nil图像查看下载新图像或使用占位符图像

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
}

更新: From apple doc

  

如果UITableViewCell对象是可重用的 - 也就是说,它具有重用标识符 - 在从UITableView方法返回对象之前调用此方法   dequeueReusableCell(withIdentifier :)   。出于性能原因,您应该仅重置与内容无关的单元格属性,例如,alpha,编辑和选择状态。表格视图的代表   的tableView(_:cellForRowAt :)    重复使用单元格时应始终重置所有内容。如果单元对象没有关联的重用标识符,则不会调用此方法。如果重写此方法,则必须确保调用超类实现。

所以请放入UserListTableViewCell班级

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

答案 1 :(得分:3)

另一种解决方案

您可以使用UIImage(data: Data(contentsOf: URL(string: urlString)!))从url检索图像,但它占用主线程,因此需要缓存。 此解决方案不会显示错误图像,但不会很好,因为初始化会导致延迟。

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 {
                    URLImageRetriever.cache.setObject(image, forKey: urlString as AnyObject)
                }
            } catch {

            }
        }
        return image
    }
}

<强>来源

您的扩展程序loadImageFromCacheWithUrlString可能不适合这种情况。

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

  1. 您的ImageView a要求显示url-a的图片。现在有一个URLSession task-a正在运行。
  2. 您的UITableViewCell会重复使用以显示另一个数据源,因此您的ImageView a会请求显示url-b的图像。现在有一个URLSession task-b正在运行。
  3. task-burl-b检索到的图片。您的ImageView a现在可以完美显示url-b的图片。
  4. tast-a稍后的图片。您的ImageView设置为显示图像格式url-a。让我们感到困惑。
  5. 每当您检索图片时,都应该检查想要使用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)")
            }
        }
    }
    

答案 2 :(得分:1)

你可以这样试试吗

var user = JSON.parse('{"firstname": "Mr.", "lastname": "My Father's Son"}');