@Shakedown

Почему сменяются ячейки в UICollectionView?

У меня есть UICollectionView, ячейки которой конфигурируются в зависимости от данных, полученных из сети.

Вот метод cellForItemAt:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        print(indexPath.row)
            
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "searchingCell", for: indexPath) as! SearchingCollectionViewCell
        cell.configure(stringUrl: images[indexPath.row].fullUrl)
        
        return cell
    }

Когда я начинаю листать мою collection view, то картинка, допустим, с 1 ячейки становится на 10 ячейку. Это происходит, потому что метод вызывается постоянно при листании collection view, и приходить могут разные indexPath. Если я перехожу в начало collection view, то почему-то приходит ненулевой indexPath, то есть на 1 ячейку встает картинка с другой ячейки. Я пробовал создавать массив ячеек, заполнять его, и, если приходит нужный indexPath, возвращать ячейку. Но проблема в том, что indexPath ведет себя очень странно. Можно ли это поправить каким-нибудь адекватным способом?
  • Вопрос задан
  • 176 просмотров
Решения вопроса 2
briahas
@briahas
ObjC, Swift, Python
Переосмыслите свой подход к заполнению ячеек контентом. Т.е. либо
- на момент заполнения контент уже должен быть, и достаточно его просто разместить в ячейке;
либо
- (если первый вариант невозможен) заполнять ячейку "пустым" контентом, а параллельно грузить контент асинхронно, но прерывать загрузку контента если ячейка ушла из видимого диапазона.
Ответ написан
Если я правильно понял, вы передаете в ячейку ссылку на изображение, и дальше асинхронно загружаете изображение с этого адреса.

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

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

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

Я предлагаю следующее решение:
Ставим наблюдателя, при получении строки с адресом изображения будет проверять, можно ли сформировать ссылку из указанного адреса, если не получится - тогда поставит дефолтное изображение

var imageURLString:   String? {
        
        didSet {
            
            DispatchQueue.global(qos: .utility).async { [unowned self] in
                
                guard imageURLString    != "",
                      let imageURLString = self.imageURLString,
                      let imageURL       = URL(string: imageURLString) else {
                          
                          DispatchQueue.main.async {
                              
                              self.ImageView.image = UIImage(named: "default")
                              
                          }
                          
                          return
                          
                      }
                
                loadImage(imageURL: imageURL)
                
            }
            
            
        }
        
    }


Если ссылка получена, вызывается этот метод, который получит данные и подставит в изображение, либо поставит дефолтную картинку, если данные не пришли по тем или иным причинам

fileprivate func loadImage(imageURL: URL)  {
        
        dataTask = URLSession.shared.dataTask(with: imageURL, completionHandler: { data, response, error in
            
            if let data = data,
               let image = UIImage(data: data) {
                
                DispatchQueue.main.async { [unowned self] in
                    
                    self.ImageView.image = image
                    
                }
                
            } else {
                
                DispatchQueue.main.async { [unowned self] in
                    
                    self.ImageView.image = UIImage(named: "default")
                    
                }
                
            }
            
        })
        
        dataTask?.resume()
        
    }


Самое главное. Метод остановит загрузку если ячейка была удалена из области видимости.

override func prepareForReuse()            {
        super.prepareForReuse()
        
        dataTask?.cancel()
        
        ImageView.image = UIImage(named: "default")
    }


Код выше - прописан в классе ячейки коллекции.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы