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

Posted

技术标签:

【中文标题】Swift 3:tableView 复制从 Firebase 加载的图像【英文标题】:Swift 3 : tableView duplicating loaded images from Firebase 【发布时间】:2017-07-24 15:37:08 【问题描述】:

我正在尝试显示带有图像的用户列表。这是代码

    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 文件中,我正在下载图像(也使用缓存)并将其显示在桌子上。但我看到有些行有重复/重复的图像。重新加载后,重复项消失,但在垂直滚动时返回。

发生这种情况:

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



【问题讨论】:

你为cell.message调用的代码写了什么? 重用cell set nil image view 在 UserListTableViewCell 中分享您的图像查看代码 @Larme:分享代码。 @NazmulHasan:你现在可以看看了吗? 【参考方案1】:

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

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 

【讨论】:

你会试试这条线cell.userImage.image = UIImage(named: "") 仍然没有运气 这里有另外 2 个回答,那是怎么回事 @snowwalker 你确定你的图片在 firebase 数据库或图片 url 上不一样 @snowwalker 请userImage 将子视图添加到cellcontentView 属性。添加子视图时,您负责定位这些视图并自行设置其内容。【参考方案2】:

另一种解决方案

你可以使用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 可能不适合这种情况。

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

    您的 ImageView a 请求显示 url-a 的图像。现在有一个 URLSession task-a 正在运行。 您的 UITableViewCell 被重用于呈现另一个数据源,因此您的 ImageView a 请求呈现 url-b 的图像。现在有一个 URLSession task-b 正在运行。 task-burl-b 检索到的图片。您的 ImageView a 现在可以完美地显示来自 url-b 的图像。 tast-a 稍后获取图片。你的 ImageView 被设置为显示图像形式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)")
        
    

【讨论】:

我可以使用 .resume() 运行代码,但仍然遇到图像重复问题。 感谢您的帮助。我试过了,但是太慢了。关于做同样事情的不同方式的任何其他建议?【参考方案3】:

你可以这样试试吗

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

【讨论】:

以上是关于Swift 3:tableView 复制从 Firebase 加载的图像的主要内容,如果未能解决你的问题,请参考以下文章

多个位置的相同TableView swift 3

如何在 Swift 3 中使用 JSON 填充 TableView

使用swift3在tableview中搜索栏和部分

从 Firebase 下载 json 并填充 TableView Swift 3

如何在 swift 3 中将图像从服务器加载到 tableView 单元格?

使用 Swift 在 tableview 中使用 headers 部分进行复制