从 Firebase 加载后单元格不显示图像 Swift
Cell not showing image after loaded from Firebase Swift
我有一个显示图像或文本的 UITableViewCell。我已将我的 UIImageView 高度和宽度设置为 <= 150,以便在图像不存在但文本存在的情况下它不会影响单元格高度。 (这是一个消息传递应用程序)
现在我的问题是,单元格在加载后不显示图像,用户需要返回并再次查看此屏幕才能看到图像。我该如何解决?
图片加载前:
图片加载成功后:
如您所见,它没有显示,只是展开了视图
如果用户按下 BACK 并再次出现在该视图中,
图像现在显示。
我该如何解决这个问题?因为我无法在 tableviewcell 中写入 tableview.reloaddata()
。
在我的 tableviewcell 中配置我的单元格的代码:
func configCell(message: Message) {
self.message = message
if message.fromId == currentUser {
sentView.isHidden = false
sentMsgLabel.text = message.textMessages
receivedMsgLabel.text = ""
receivedView.isHidden = true
timeReceived.text = ""
timeSent.text = message.timestamp
youLabel.text = "You"
themLabel.text = ""
if let ImageUrl = message.imageUrlLink {
self.receivedimg.loadImageUsingCacheWithUrlString(ImageUrl)
}
}
else {
sentView.isHidden = true
sentMsgLabel.text = ""
receivedMsgLabel.text = message.textMessages
receivedMsgLabel.isHidden = false
timeReceived.text = message.timestamp
timeSent.text = ""
// setting name for receipient
Database.database().reference().child("users").child(message.fromId!).child("name").observe(.value) { (datasnapshot) in
if let name = datasnapshot.value as? String {
self.themLabel.text = name
}
}
youLabel.text = ""
}
}
我的 loadImageUsingCacheWithUrlString 代码将是如下扩展:
let imageCache = NSCache<AnyObject, AnyObject>()
extension UIImageView {
func loadImageUsingCacheWithUrlString(_ urlString: String) {
self.image = nil
//check cache for image first
if let cachedImage = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
self.image = cachedImage
return
}
//otherwise fire off a new download
let url = URL(string: urlString)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
//download hit an error so lets return out
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
}
})
}).resume()
}
}
与其在 UITableViewCell
中执行操作,不如在 cellForRowAt
中执行操作
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//Your actuions here
}
然后你可以在你的 UITableViewController
中使用 tableView.reloadData()
希望这对解决您的问题有所帮助。
下载图片后你的问题很明显,你的单元格没有及时更新,试试你的单元格中的self.setNeedDisplays()。它告诉您的单元格应该在下一个查看周期更新。
我建议使用 KingFisher 框架,而不是自己加载和捕获图像,它几乎可以完成我们处理来自远程服务器的图像所需的一切:下载、捕获、预取...以及 立即显示您的图像下载后
我试了很多方法,最后..在我的ViewDidload中,我只是加了一个延迟
DispatchQueue.main.asyncAfter(deadline: .now() + 1){
self.tableView.reloadData()
}
需要延迟。没有它,它是行不通的。我假设这是由于在图像加载完成之前设置并加载了单元格高度,所以 tablecell 不知道需要多少高度,这就是为什么我们需要在延迟 1 秒后重新加载 tableview
似乎这段代码被执行了一次,但您的 UITableView 仍在加载单元格,因为计数显然是 2。当您将 Message 对象传递给 UICollectionViewCell
class 在 cellForRowAt indexPath
方法中,您需要对该消息使用 didSet
,然后调用您的函数。这意味着每次传入消息和传入消息时,didSet 内部的内容都会被执行。
在你的 UICollectionViewCell 中 class:
var message: Message? {
didSet {
configCell(message)
}
}
Class 与你的 UITableViewController:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = // your cell here dequeue cell and downcast as your cell class e.g as! MyCell
cell.message = yourMessagesArray[indexPath.row]
return cell
}
我遇到了同样的问题,我就是这样解决的。
在您的 ViewController 中创建一个函数,该函数将 动态更新每个单元格 :
private func reloadRowAt(_ indexPath: IndexPath) {
self.tableView.beginUpdates()
self.tableView.reloadRows( at: [indexPath], with: .fade)
self.tableView.endUpdates()
}
现在在 cellForRowAtindexPath() 中使用它:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: identifier) as? MyCustomCell else { return UITableViewCell()}
// Download and refresh cell
let image = imageDownloader.loadImage(from: url) {
self.reloadRowAt(indexPath)
}
cell.image = image
return cell
}
现在查看代码{ self.reloadRowAt(indexPath) }
,这是下载图像的方法的完成处理程序,一旦图像被下载到单元格returns。
func loadImage(from url: URL, completion: @escaping () -> Void ) -> UIImage {
let size = CGSize(width: 100, height: 140)
// Download and Compress image to fit container size
DispatchQueue.global(qos: .background).async {
guard let newimage = self.downloadAndCompress(url: url, newSize: size) else { return }
// Show in UI
DispatchQueue.main.async {
completion()
}
}
return UIImage()
}
另外,我用self.downloadAndCompress
的方法将下载的图片压缩成新的大小。
查看完整代码:
https://gist.github.com/richimf/fad8d320fbfb5059c5472e0f55ea24c2
我有一个显示图像或文本的 UITableViewCell。我已将我的 UIImageView 高度和宽度设置为 <= 150,以便在图像不存在但文本存在的情况下它不会影响单元格高度。 (这是一个消息传递应用程序)
现在我的问题是,单元格在加载后不显示图像,用户需要返回并再次查看此屏幕才能看到图像。我该如何解决?
图片加载前:
图片加载成功后:
如您所见,它没有显示,只是展开了视图
如果用户按下 BACK 并再次出现在该视图中,
图像现在显示。
我该如何解决这个问题?因为我无法在 tableviewcell 中写入 tableview.reloaddata()
。
在我的 tableviewcell 中配置我的单元格的代码:
func configCell(message: Message) {
self.message = message
if message.fromId == currentUser {
sentView.isHidden = false
sentMsgLabel.text = message.textMessages
receivedMsgLabel.text = ""
receivedView.isHidden = true
timeReceived.text = ""
timeSent.text = message.timestamp
youLabel.text = "You"
themLabel.text = ""
if let ImageUrl = message.imageUrlLink {
self.receivedimg.loadImageUsingCacheWithUrlString(ImageUrl)
}
}
else {
sentView.isHidden = true
sentMsgLabel.text = ""
receivedMsgLabel.text = message.textMessages
receivedMsgLabel.isHidden = false
timeReceived.text = message.timestamp
timeSent.text = ""
// setting name for receipient
Database.database().reference().child("users").child(message.fromId!).child("name").observe(.value) { (datasnapshot) in
if let name = datasnapshot.value as? String {
self.themLabel.text = name
}
}
youLabel.text = ""
}
}
我的 loadImageUsingCacheWithUrlString 代码将是如下扩展:
let imageCache = NSCache<AnyObject, AnyObject>()
extension UIImageView {
func loadImageUsingCacheWithUrlString(_ urlString: String) {
self.image = nil
//check cache for image first
if let cachedImage = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
self.image = cachedImage
return
}
//otherwise fire off a new download
let url = URL(string: urlString)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
//download hit an error so lets return out
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
}
})
}).resume()
}
}
与其在 UITableViewCell
中执行操作,不如在 cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//Your actuions here
}
然后你可以在你的 UITableViewController
tableView.reloadData()
希望这对解决您的问题有所帮助。
下载图片后你的问题很明显,你的单元格没有及时更新,试试你的单元格中的self.setNeedDisplays()。它告诉您的单元格应该在下一个查看周期更新。
我建议使用 KingFisher 框架,而不是自己加载和捕获图像,它几乎可以完成我们处理来自远程服务器的图像所需的一切:下载、捕获、预取...以及 立即显示您的图像下载后
我试了很多方法,最后..在我的ViewDidload中,我只是加了一个延迟
DispatchQueue.main.asyncAfter(deadline: .now() + 1){
self.tableView.reloadData()
}
需要延迟。没有它,它是行不通的。我假设这是由于在图像加载完成之前设置并加载了单元格高度,所以 tablecell 不知道需要多少高度,这就是为什么我们需要在延迟 1 秒后重新加载 tableview
似乎这段代码被执行了一次,但您的 UITableView 仍在加载单元格,因为计数显然是 2。当您将 Message 对象传递给 UICollectionViewCell
class 在 cellForRowAt indexPath
方法中,您需要对该消息使用 didSet
,然后调用您的函数。这意味着每次传入消息和传入消息时,didSet 内部的内容都会被执行。
在你的 UICollectionViewCell 中 class:
var message: Message? {
didSet {
configCell(message)
}
}
Class 与你的 UITableViewController:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = // your cell here dequeue cell and downcast as your cell class e.g as! MyCell
cell.message = yourMessagesArray[indexPath.row]
return cell
}
我遇到了同样的问题,我就是这样解决的。
在您的 ViewController 中创建一个函数,该函数将 动态更新每个单元格 :
private func reloadRowAt(_ indexPath: IndexPath) {
self.tableView.beginUpdates()
self.tableView.reloadRows( at: [indexPath], with: .fade)
self.tableView.endUpdates()
}
现在在 cellForRowAtindexPath() 中使用它:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: identifier) as? MyCustomCell else { return UITableViewCell()}
// Download and refresh cell
let image = imageDownloader.loadImage(from: url) {
self.reloadRowAt(indexPath)
}
cell.image = image
return cell
}
现在查看代码{ self.reloadRowAt(indexPath) }
,这是下载图像的方法的完成处理程序,一旦图像被下载到单元格returns。
func loadImage(from url: URL, completion: @escaping () -> Void ) -> UIImage {
let size = CGSize(width: 100, height: 140)
// Download and Compress image to fit container size
DispatchQueue.global(qos: .background).async {
guard let newimage = self.downloadAndCompress(url: url, newSize: size) else { return }
// Show in UI
DispatchQueue.main.async {
completion()
}
}
return UIImage()
}
另外,我用self.downloadAndCompress
的方法将下载的图片压缩成新的大小。
查看完整代码:
https://gist.github.com/richimf/fad8d320fbfb5059c5472e0f55ea24c2