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
将子视图添加到cell
的contentView
属性。添加子视图时,您负责定位这些视图并自行设置其内容。【参考方案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-b
从 url-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 加载的图像的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Swift 3 中使用 JSON 填充 TableView
从 Firebase 下载 json 并填充 TableView Swift 3