【问题标题】:Displaying image from database in TableView bugs在 TableView 错误中显示数据库中的图像
【发布时间】:2016-10-17 14:53:13
【问题描述】:

我有一个 TableView,我正在用从数据库中检索到的数据填充它。除图像外,一切正常。由于单元重用行为,我在cellForRowAtIndexPath 中获取图像。我选择在cellForRowAtIndexPath 中获取图像,因为在详细信息检索功能(在viewDidLoad 中触发)中,我需要执行另一个请求,这会导致其他问题(在存储图像 url 之前重新加载 tableview)

问题是当我快速滚动时,可重复单元格在显示用户图像时出错

override func viewDidLoad() {
    super.viewDidLoad()
    fetchData()
}

var theUser = 

func fetchData() {
   //.. after data is retrieved

   var innerDict = [String:String]()

   if let user = details.value![key] {
      if let name = user["name"] {
         // works
         innerDict["name"] = name
       }

      if let image = user["imageName"] {
       // gets the image name but at this point I need to;
       // a) retrieve the url here (with another call), which will eventually fail
       // to catch up with `innerDict` so `innerDict` won't contain `image` variable.
       // ie;

       myRef.downloadURLWithCompletion { (URL, error) -> Void in }

      // b) Store the `image` name in innerDict and download image from url 
      // in `cellForRowAtIndexPath`. I chose this method:

        innerDict["image"] = image
      }

   user[id] = innerDict
   tableView.reloadData()
}

现在tableView照常了。

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

   let cell = ...

   // more stuff

   if let imageName = user["image"] {
      let storage = FIRStorage.storage()
      let storageRef = storage.referenceForURL("gs://bucket.com").child(user[id]).child(imageName)

      storageRef.downloadURLWithCompletion { (URL, error) -> Void in
           if (error != nil) {
              // handle 
           } else {
              // I thought using Haneke would help to cache the image
               cell.image.hnk_setImage(URL!)
           }
      }
 }

这是我能到达的最近的一个。但是,当我快速滚动时,图像会显示错误。


编辑:

我也尝试过使用这种方法,但是使用这种方法会多次下载同一张图片,因此显示相同的图片需要一些时间。

islandRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
  if (error != nil) {
    // Uh-oh, an error occurred!
  } else {
    let image: UIImage! = UIImage(data: data!)
    cell.userImage.hnk_setImage(image, key: "\(userID)")
  }
}

但是,使用顶部方法时,速度非常快。上面代码的唯一问题是我快速滚动时出现的故障。


编辑 2

 var images = [UIImage]()

 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCellWithIdentifier("ItemCell", forIndexPath: indexPath) as! ItemDetailTableViewCell

let item = items[indexPath.section][indexPath.row]

if let uid = item["owner"] as? String {
   if let user = users[uid] {
      if let imageName = user["image"] { 
         if let img: UIImage = images[indexPath.row] {    // crash here "fatal error: Index out of range"
           cell.userImage.image = img
          }
      } else {
        let storage = FIRStorage.storage()
        let storageRef = storage.referenceForURL("gs://bucket").child(uid).child(imageName)

        storageRef.downloadURLWithCompletion { (URL, error) -> Void in
           if (error != nil) {

            } else {
               dispatch_async(dispatch_get_main_queue(), {
                     cell.userImage.hnk_setImageFromURL(URL!)
                     self.images[indexPath.row] = cell.image.image!
               })
           }
         }
    }   
}





}    

【问题讨论】:

  • 这是因为你总是从网上加载图片,你应该在第一时间保存图片并在滚动时显示
  • 那么您是否建议我将图像下载为 NSData 并将它们存储在不同的数组中?哪个地方最适合下载+保存图片?
  • 是的,这个故障的发生是因为每次单元格要重新使用时都会下载图像
  • 但是这种方法存在问题。由于图像下载将是一个不同的异步任务,tableView.reload() 将在图像下载成功之前触发。这最终不会显示图像,我错了吗?
  • 出现此错误是因为您没有缓存图像。尝试使用 SDWebImage 之类的框架。

标签: ios swift uitableview firebase firebase-storage


【解决方案1】:

我认为您应该从 url 保存图像并在单元格将要重用时显示图像,希望这会解决您的故障

 var myImages =  [String: UIImage]()


 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
     let cell = tableView.dequeueReusableCellWithIdentifier("ItemCell", forIndexPath: indexPath) as! ItemDetailTableViewCell
     let item = items[indexPath.section][indexPath.row]

    if let img: UIImage = myImages["\(indexPath.section)\(indexPath.row)"] {
        cell.image.image = img
    } else {
      if let uid = item["owner"] as? String {
        if let imageName = user["image"] {
           let storage = FIRStorage.storage()
           let storageRef = storage.referenceForURL("gs://bucket.com").child(user[id]).child(imageName)
           storageRef.downloadURLWithCompletion { (URL, error) -> Void in
               if (error != nil) {
                  cell.image = UIImage(named: "placeholder") // put default Image when failed to download Image
               } else {
                  dispatch_async(dispatch_get_main_queue(), {
                   cell.image.hnk_setImage(URL!)
                   // Store the image in to our cache
                   self.myImages["\(indexPath.section)\(indexPath.row)"]= cell.image.image
                })
              }
         }
      }
    }

}

【讨论】:

  • 问题是,我使用的是 Firebase 存储,我想我需要用不同的请求检索 url;然后执行下载过程(如a))。或者使用this method直接下载。但是,对于第二个选项,我不知道如何将它放在后台线程中。你能指导我吗?不胜感激
  • 好的,现在试试 :),我已经根据 firebase 解决了这个问题
  • 感谢您的宝贵时间。但是您是如何获得 cell.image 的?它迫使我打开包装,然后给我nil 错误
  • self.myImages[indexPath.row]= cell.image。如果您想将myImages 保留为[UIImage],它应该是cell.image.image,但它仍然会引发错误。致命错误:意外发现 nil.. 我认为您存储在 myImages 中并且也没有使用它作为回报
  • Opzz 我的错,你的 UIImage 名称工资图像,这不是 UIImagew 的好名字:D,已修复,现在检查
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-21
  • 2016-12-06
相关资源
最近更新 更多