Дублирующаяся ячейка CollectionView при загрузке дополнительных данных

Проблема:

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

Кто-нибудь может понять, почему это происходит?

ПРИМЕЧАНИЕ. Массив с изображениями не имеет дубликатов.

Видео проблемы: https://www.youtube.com/watch?v=vjRsFc8DDmI

Изображение проблемы:

CollectionView имеет повторяющуюся ячейку при загрузке дополнительных данных

Вот мои функции для collectionView:

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    //#warning Incomplete method implementation -- Return the number of items in the section
    if self.movies == nil
    {
      return 0

    }

    return self.movies!.count

}

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell 
{
    let cell =    
      collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, 
        forIndexPath: indexPath) as! UpcomingCollectionViewCell

   if self.movies != nil && self.movies!.count >= indexPath.row
    {
        // Calc size of cell
        cell.frame.size.width = screenWidth / 3
        cell.frame.size.height = screenWidth / 3 * 1.54

        let movies = self.movies![indexPath.row]
        if(movies.posterPath != "" || movies.posterPath != "null"){
        cell.data = movies.posterPath
        }
        else{
        cell.data = nil
        }
        // See if we need to load more movies
        let rowsToLoadFromBottom = 5;
        let rowsLoaded = self.movies!.count
        if (!self.isLoadingMovies && (indexPath.row >= (rowsLoaded - rowsToLoadFromBottom)))
        {
            let totalRows = self.movieWrapper!.totalResults!
            let remainingMoviesToLoad = totalRows - rowsLoaded;
            if (remainingMoviesToLoad > 0)
            {
                self.loadMoreMovies()
            }
        }
}
   else
{
        cell.data = nil
}

    return cell
}

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize
{
    return CGSize(width: screenWidth/3, height: screenWidth/3*1.54)
}

Здесь я загружаю данные из класса Wrapper:

func loadFirstMovies()
{

    isLoadingMovies = true

    Movies.getMovies({ (movieWrapper, error) in
        if error != nil
        {
            // TODO: improved error handling
            self.isLoadingMovies = false
            let alert = UIAlertController(title: "Error", message: "Could not load first movies \(error?.localizedDescription)", preferredStyle: UIAlertControllerStyle.Alert)
            alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
            self.presentViewController(alert, animated: true, completion: nil)
        }
        self.addMoviesFromWrapper(movieWrapper)
        self.activityIndicator.hidden = true
        self.isLoadingMovies = false
        self.collectionView.reloadData()
    })
}

func loadMoreMovies(){

    self.isLoadingMovies = true
    if self.movies != nil && self.movieWrapper != nil && self.movieWrapper!.page < self.movieWrapper!.totalPages
    {
        // there are more species out there!
        Movies.getMoreMovies(self.movieWrapper, completionHandler: { (moreWrapper, error) in
            if error != nil
            {
                // TODO: improved error handling
                self.isLoadingMovies = false
                let alert = UIAlertController(title: "Error", message: "Could not load more movies \(error?.localizedDescription)", preferredStyle: UIAlertControllerStyle.Alert)
                alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
                self.presentViewController(alert, animated: true, completion: nil)
            }
            print("got more!")
            self.addMoviesFromWrapper(moreWrapper)
            self.isLoadingMovies = false
            self.collectionView.reloadData()
        })
    }
}

func addMoviesFromWrapper(wrapper: MovieWrapper?)
{
    self.movieWrapper = wrapper
    if self.movies == nil
    {
        self.movies = self.movieWrapper?.results
    }
    else if self.movieWrapper != nil && self.movieWrapper!.results != nil
    {
        self.movies = self.movies! + self.movieWrapper!.results!
    }
}

И напоследок звоню: loadFirstMovies() в viewDidLoad()

РЕДАКТИРОВАТЬ: UpcomingCollectionViewCell

class UpcomingCollectionViewCell: UICollectionViewCell {

@IBOutlet weak var imageView: UIImageView!


var data:String?{
    didSet{
        self.setupData()
    }
}

func setupData(){

   self.imageView.image = nil // reset the image

    if let urlString = data{
        let url =  NSURL(string: "http://image.tmdb.org/t/p/w342/" + urlString)
        self.imageView.hnk_setImageFromURL(url!)


      }
    }  
 }

person Mat0    schedule 12.11.2015    source источник
comment
пожалуйста, покажите код вашего UpcomingCollectionViewCell...   -  person André Slotta    schedule 12.11.2015
comment
Я обновил вопрос с помощью UpcomingCollectionViewCell.   -  person Mat0    schedule 12.11.2015


Ответы (2)


Это типичная проблема настройки представления таблицы/коллекции.

Каждый раз, когда вы перерабатываете ячейку с помощью метода удаления из очереди, такого как dequeueReusableCellWithReuseIdentifier:, вы всегда должны полностью настраивать все представления в ячейке, в том числе устанавливать для всех текстовых полей/представлений изображений их начальные значения. В вашем коде есть несколько операторов if, где, если условие if ложно, вы не настраиваете представления в ячейке. У вас должны быть предложения else, которые удаляют старый контент из представлений ячейки на случай, если там остался контент, оставшийся с момента последнего использования ячейки.

**РЕДАКТИРОВАТЬ:

Измените метод cellForItemAtIndexPath, чтобы он начинался так:

func collectionView(collectionView: UICollectionView, 
  cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell 
{
    let cell =    
      collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, 
        forIndexPath: indexPath) as! UpcomingCollectionViewCell
    cell.imageView.image = nil;  //Remove the image from the recycled cell
//The rest of your method ...
person Duncan C    schedule 12.11.2015
comment
Я отредактировал ответ с операторами else в cellForItemAtIndexPath. Это все еще приводит к дублированию некоторых ячеек. А не бывает ли так часто по сравнению с прежним (без остального)? - person Mat0; 12.11.2015
comment
Чтобы это работало, вам также понадобится изменение Андре Слотты в setupData (где вы устанавливаете изображение imageView равным нулю перед загрузкой нового изображения). - person Duncan C; 12.11.2015
comment
Я также добавил изменение Андре Слотты, но теперь ячейки пусты и не загружены новым изображением. Я снял короткое видео о его текущем состоянии, пожалуйста, смотрите: youtube.com/watch?v= vjRsFc8DDmI. Код также обновлен - person Mat0; 12.11.2015
comment
Это сработало! Но я не понимаю, почему это не работало раньше :) - person Mat0; 12.11.2015
comment
Внимательно перечитайте мой первоначальный ответ. Я сказал тебе, что тебе нужно было сделать. ВЫ ДОЛЖНЫ ВСЕГДА НАСТРАИВАТЬ ПЕРЕРАБОТАННУЮ ЯЧЕЙКУ ПОЛНОСТЬЮ. В вашем коде были пути, которые этого не сделали, и в этих случаях изображение, оставшееся с момента последнего использования ячейки, все равно будет в переработанной ячейке. . В коде, который я добавил, я явно установил для ячейки imageView.image значение nil перед началом операторов if. Таким образом, представление изображения всегда начинается с нуля, независимо от того, как код проходит через ваши операторы if. - person Duncan C; 12.11.2015
comment
Примечание. Эта проблема возникает не раз почти у всех разработчиков iOS. Требуется некоторое время, чтобы привыкнуть. - person Duncan C; 12.11.2015
comment
Спасибо за помощь в решении этого - person Mat0; 12.11.2015
comment
Если я также очищаю изображения в вызове collectionView(..), некоторые из моих изображений (при быстром пролистывании) становятся пустыми. Может быть, потому что я сбрасываю их на ноль уже непосредственно перед загрузкой с URL-адреса? - person Michele Dall'Agata; 27.07.2018
comment
почему бы просто не скрыть ячейку? - person KING; 11.09.2018

в ваших пользовательских ячейках setupData() попробуйте следующее:

func setupData(){
  self.imageView.image = nil // reset the image

  if let urlString = data{
    let url =  NSURL(string: "http://image.tmdb.org/t/p/w342/" + urlString)
    self.imageView.hnk_setImageFromURL(url!)
  }
}
person André Slotta    schedule 12.11.2015
comment
Это очищает изображение ячеек, но приводит к тому, что некоторые ячейки остаются пустыми. - person Mat0; 12.11.2015
comment
ну... если ЕСТЬ urlString, который ведет к реальному файлу изображения, и метод hnk_setImageFromURL работает так, как ожидалось, этот код определенно должен работать. - person André Slotta; 12.11.2015
comment
hnk_setImageFromURL взят из библиотеки Haneke, поэтому я ожидаю, что это сработает, а urlString также приведет к реальному файлу изображения? - person Mat0; 12.11.2015
comment
Вы хотите очистить изображение для начала, а затем позволить вашему вызову hnk_setImageFromURL установить изображение после его загрузки. - person Duncan C; 12.11.2015