Как загрузить изображение асинхронно с помощью Swift с помощью UIImageViewExtension и предотвратить дублирование изображений или неправильные изображения, загруженные в ячейки

Я разрабатываю библиотеку загрузки изображений в Swift 4, что-то вроде Kingfisher с некоторыми расширениями для поддержки загрузки изображений с URL-адреса в UIImageView.

Итак, я могу использовать это расширение в ячейке UICollection или UITableview с UIImageView следующим образом:

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: 
  "collectioncell", for: indexPath)

    if let normal = cell as? CollectionViewCell {
    normal.imagview.loadImage(fromURL:imageURLstrings[indexPath.row])
  }

В основном это мой код расширения, реализующий базовую задачу URLSession dataTask и кэширование.

public extension UIImageView {
public func loadImage(fromURL url: String) {
    guard let imageURL = URL(string: url) else {
        return
    }

    let cache =  URLCache.shared
    let request = URLRequest(url: imageURL)
    DispatchQueue.global(qos: .userInitiated).async {
        if let data = cache.cachedResponse(for: request)?.data, let 
           image = UIImage(data: data) {
             DispatchQueue.main.async {
                self.image = image
            }
        } else {
            URLSession.shared.dataTask(with: request, 
       completionHandler: { (data, response, error) in

            print(response.debugDescription)
                print(data?.base64EncodedData() as Any)
                print(error.debugDescription)
                if let data = data, let response = response, ((response as? HTTPURLResponse)?.statusCode ?? 500) < 300, let image = UIImage(data: data) {
                    let cachedData = CachedURLResponse(response: response, data: data)
                    cache.storeCachedResponse(cachedData, for: request)
                    DispatchQueue.main.async {
                       self.image = image
                    }
                }
            }).resume()
        }
    }
}



 }

В заключение я обнаружил, что иногда, если мой URL-адрес сломан, или я получаю 404, или даже если я прокручиваю UICollectionView до того, как все изображения будут полностью загружены, изображения загружаются в неправильную ячейку или я иногда нахожу повторяющиеся изображения в collectionView, но этого не происходит если я использую Kingfisher.

Это мои результаты

Это зимородки

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


person BigFire    schedule 11.05.2019    source источник
comment
@rob, ты можешь мне помочь? stackoverflow.com/questions/57137326/   -  person BigFire    schedule 22.07.2019


Ответы (1)


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

Когда вы запускаете новый запрос на просмотр изображения, предполагая, что вы не нашли изображение в кеше сразу, перед запуском сетевого запроса вы должны (а) удалить все предыдущие изображения (как предложил Брэндон); (б) возможно загрузить изображение-заполнитель или UIActivityIndicatorView; и (c) отменить любой предыдущий запрос изображения для этого просмотра изображения. Только после этого следует начинать новый запрос.

Что касается того, как вы сохраняете ссылку на предыдущий запрос в расширении, вы не можете добавлять сохраненные свойства, но можете использовать _ 2_, чтобы сохранить задачу сеанса при запуске сеанса, установите для него значение nil по завершении запроса и _ 4_ при получении объекта сеанса, чтобы узнать, нужно ли вам отменить предыдущий.

(Между прочим, Kingfisher оборачивает эту связанную логику объекта в свой вычисляемое свойство для идентификатора задачи. Это прекрасный способ сохранить и получить этот идентификатор задачи.


Что касается неудачных запросов, то тот факт, что вы выполняете необузданные запросы изображений, может вызвать эту проблему. Прокрутите немного, и ваши запросы будут задерживаться и будут задерживаться. Выполнение отмены (см. Выше) уменьшит эту проблему, но в конечном итоге все же может произойти. Если после исправления вышеизложенного у вас по-прежнему возникают сбои запросов, возможно, вам придется ограничить количество одновременных запросов. Распространенное решение - заключить запросы в асинхронный подкласс Operation и добавить их в OperationQueue с помощью maxConcurrentOperationCount. Или, если вы ищете дешевое и выгодное решение, вы можете попробовать увеличить порог тайм-аута в своих запросах.

person Rob    schedule 12.05.2019
comment
Удаление любого предыдущего изображения, как предложил Брэндон, было недостаточно для решения проблемы, и я не знаю, почему он удалил свой ответ, однако я смог отследить объект сеанса и отменил любую предыдущую задачу, как вы предложили, что действительно решило проблему, Спасибо. - person BigFire; 12.05.2019
comment
Наблюдение Брэндона является важной частью решения (если вы не сбросите изображение сразу, вы можете временно увидеть старое изображение при извлечении нового изображения), но, как вы обнаружили, это лишь небольшая часть всего решения. - person Rob; 12.05.2019
comment
Я нажал галочку и получил ответ. Спасибо за отзыв! Голоса, отданные теми, у кого репутация менее 15, записываются, но не изменяют общедоступный рейтинг публикации. - person BigFire; 12.05.2019
comment
ссылка @ Какая-нибудь помощь с этим ограбить? - person BigFire; 12.05.2019
comment
@big fire не могли бы вы поделиться кодом после устранения проблемы, потому что я столкнулся с аналогичной проблемой здесь: дублирование изображения или загрузка неправильного изображения - person Mohammad Yunus; 13.05.2019
comment
@MohammadYunus, проверьте этот ответ поищите ответ Робса. - person BigFire; 13.05.2019