2015-09-02 5 views
2

В моем приложении я построил собственный класс загрузки асинхронных изображений. Я передаю объект, затем он проверяет, имеет ли кэш (NSCache) изображение, если он не будет проверять файловую систему, если изображение уже сохранено. Если изображение уже не сохранено, оно будет загружать изображение в фоновом режиме (Справка NSOperations).UITableViewCell асинхронно загружает проблемы с изображениями - Swift

Это прекрасно работает до сих пор, но я столкнулся с несколькими небольшими проблемами, когда изображение таблицы загружает изображения.

Во-первых, это функция я использую, чтобы настроить вид таблицы ячейки из tableView(tableView:, willDisplayCell:, forRowAtIndexPath:)

func configureCell(cell: ShowTableViewCell, indexPath: NSIndexPath) { 

    // Configure cell 
    if let show = dataSource.showFromIndexPath(indexPath) { 

     ImageManager.sharedManager.getImageForShow(show, completionHandler: { (image) -> Void in 
      if self.indexPathsForFadedInImages.indexOf(indexPath) == nil { 
       self.indexPathsForFadedInImages.append(indexPath) 

       if let fetchCell = self.tableView.cellForRowAtIndexPath(indexPath) as? ShowTableViewCell { 
        func fadeInImage() { 
         // Fade in image 
         fetchCell.backgroundImageView!.alpha = 0.0 
         fetchCell.backgroundImage = image 
         UIView.animateWithDuration(showImageAnimationSpeed, animations: {() -> Void in 
          fetchCell.backgroundImageView!.alpha = 1.0 
         }) 
        } 

        if #available(iOS 9, *) { 
         if NSProcessInfo.processInfo().lowPowerModeEnabled { 
          fetchCell.backgroundImage = image 
         } 
         else { 
          fadeInImage() 
         } 
        } 
        else { 
         fadeInImage() 
        } 
       } 
       else { 
        // Issues are here 
       } 
      } 
      else { 
       // Set image 
       cell.backgroundImage = image 
      } 
     }) 
... 
} 

Где «// Проблемы здесь» комментарий, то есть, где я бегу на несколько вопросов.

До сих пор я не понял другого способа проверить, что изображение принадлежит ячейке, где «// Проблемы здесь». Если добавить

cell.backgroundImage = IMAGE

там, то это устраняет проблему, где иногда изображение не будет отображаться на табличной ячейке. Пока единственная причина, по которой я нашел это, заключается в том, что изображение возвращается быстрее, чем я могу вернуть ячейку представления таблицы, поэтому в представлении таблицы указано, что на этом пути указана не ячейка.

Но если я добавлю этот код там, тогда я столкнусь с другой проблемой! Ячейки будут отображать неправильные изображения, а затем он отстает от приложения, и изображение будет постоянно переключаться или даже просто оставаться на неправильном изображении.

Я проверил, что он работает по основной теме, загрузка изображений и кеширование - все в порядке. Он просто должен сделать, что в таблице говорится, что на этом пути указана не ячейка, и я попытался получить индексную ячейку для ячейки, которая возвращает также нуль.

Полу-решение этой проблемы называется tableView.reloadData() в viewWillAppear/viewDidAppear. Это устранит проблему, но затем я потеряю анимацию для ячеек таблицы на экране.

EDIT:

Если я прохожу вид изображения в getImageForShow() и установить его непосредственно будет решить эту проблему, но это менее идеальный дизайн кода. Очевидно, что вид изображения существует, ячейка существует, но по какой-то причине она не хочет работать каждый раз.

ответ

5

Таблицы повторного использования ячеек для сохранения памяти, что может вызвать проблемы с любыми асинхронными подпрограммами, которые необходимо выполнить для отображения данных ячейки (например, загрузка изображения). Если ячейка должна отображать разные данные, когда операция async завершается, приложение может неожиданно перейти в непоследовательное состояние отображения.

Чтобы обойти эту проблему, я рекомендую добавить свойство поколения к клеткам, и проверять эти свойства при асинхронной операции завершается:

protocol MyImageManager { 
    static var sharedManager: MyImageManager { get } 
    func getImageForUrl(url: String, completion: (UIImage?, NSError?) -> Void) 
} 

struct MyCellData { 
    let url: String 
} 

class MyTableViewCell: UITableViewCell { 

    // The generation will tell us which iteration of the cell we're working with 
    var generation: Int = 0 

    override func prepareForReuse() { 
     super.prepareForReuse() 
     // Increment the generation when the cell is recycled 
     self.generation++ 
     self.data = nil 
    } 

    var data: MyCellData? { 
     didSet { 
      // Reset the display state 
      self.imageView?.image = nil 
      self.imageView?.alpha = 0 
      if let data = self.data { 
       // Remember what generation the cell is on 
       var generation = self.generation 
       // In case the image retrieval takes a long time and the cell should be destroyed because the user navigates away, make a weak reference 
       weak var wcell = self 
       // Retrieve the image from the server (or from the local cache) 
       MyImageManager.sharedManager.getImageForUrl(data.url, completion: { (image, error) -> Void in 
        if let error = error { 
         println("There was a problem fetching the image") 
        } else if let cell = wcell, image = image where cell.generation == generation { 
         // Make sure that UI updates happen on main thread 
         dispatch_async(dispatch_get_main_queue(), {() -> Void in 
          // Only update the cell if the generation value matches what it was prior to fetching the image 
          cell.imageView?.image = image 
          cell.imageView?.alpha = 0 
          UIView.animateWithDuration(0.25, animations: {() -> Void in 
           cell.imageView?.alpha = 1 
          }) 
         }) 
        } 
       }) 
      } 
     } 
    } 
} 

class MyTableViewController: UITableViewController { 

    var rows: [MyCellData] = [] 

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
     var cell = tableView.dequeueReusableCellWithIdentifier("Identifier") as! MyTableViewCell 
     cell.data = self.rows[indexPath.row] 
     return cell 
    } 

} 

пары других нот:

  • Не забудьте сделать свои обновления на основном потоке.Обновление в потоке сетевой активности может привести к тому, что дисплей изменится в случайное время (или никогда)
  • Обязательно слабо ссылайтесь на ячейку (или любые другие элементы пользовательского интерфейса), когда вы выполняете операцию async в случае, если пользовательский интерфейс должен быть уничтожен до завершения async op.
+0

Я сделал это несколько иначе, но переменная генерации работает. Я сохраняю свой код configureCell там, где он есть, и просто проверяю его там, и из того, что я могу сказать, изображения всегда правильные. Спасибо :) – Maximilian

+1

У этого есть проблема, что изображения не установлены правильно при прокрутке вверх, потому что вы всегда увеличиваете значение генерации в prepareForReuse. – BalestraPatrick

Смежные вопросы