UITableView прыгает и мерцает при прокрутке - PullRequest
0 голосов
/ 27 ноября 2018

У меня есть UITableView, который отображает ячейки с изображением и текстом.Данные запрашиваются по запросу - сначала я запрашиваю данные для 10 строк, затем для следующих 10 и так далее.Я делаю это в tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath).Проблема в том, что когда я получаю данные и мне нужно обновить tableview, он иногда скачет и / или мерцает.Я звоню на reloadData.Вот часть кода:

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {

    DispatchQueue.global(qos: .background).async {
        if indexPath.row + 5 >= self.brands.count && !BrandsManager.pendingBrandsRequest {
            BrandsManager.getBrands() { (error, brands) in

                self.brands.append(contentsOf: brands as! [Brand])

                DispatchQueue.main.async { 

                    UIView.performWithoutAnimation {
                        self.brandsTableView.reloadData()
                    }

                }

            }
        }
    }

}

Высота ячеек постоянно возвращается, как здесь:

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 70
}

Я использую Kingfisher для загрузки и кэширования изображений.Вот еще немного кода из источника данных:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return brands.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifiers.ImageTableCell, for: indexPath) as! ImageTableViewCell
    let brand = brands[indexPath.row]
    cell.centerLabel.text = brand.brand
    cell.leftImageView.image = nil

    if let url = BrandsManager.brandLogoURL(forLogoName: brand.logo!) {
        let resource = ImageResource(downloadURL: url, cacheKey: url.absoluteString)
        cell.leftImageView.kf.setImage(with: resource)
    } else {
        print("Cannot form url for brand logo")
    }

    return cell
}

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

Ответы [ 2 ]

0 голосов
/ 28 ноября 2018

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

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return self.tableView(tableView, heightForRowAt: indexPath)
}

Или, если высота ячейки постоянна, вы можете сделать tableView.estimatedRowHeight = 70.0.

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

Допустим, предполагаемая высота равна 50, а реальная высота 75.Теперь, когда вы прокрутили вниз до 10 ячеек за пределами экрана, у вас есть 10*75 = 750 пикселей смещения контента.Нет, когда происходит перезагрузка, табличное представление будет игнорировать, сколько ячеек скрыто, и попытается пересчитать это.Он будет повторно использовать приблизительную высоту строки, пока не найдет путь индекса, который должен быть видимым.В этом примере он начинает вызывать ваш estimatedHeightForRow с индексами [0, 1, 2... и увеличивать offset на 50, пока не достигнет смещения вашего контента, которое все еще равно 750.Так что это означает, что он попадает в индекс 750/50 = 15.И это приводит к прыжку из ячейки 10 в ячейку 15 при перезагрузке.

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

tableView.beginUpdates()
tableView.insertRows(at: myPaths, with: .none)
tableView.endUpdates()

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

if let url = BrandsManager.brandLogoURL(forLogoName: brand.logo!) {
    if url != cell.currentLeftImageURL { // Check if new image needs to be applied
        let resource = ImageResource(downloadURL: url, cacheKey: url.absoluteString)
        cell.currentLeftImageURL = url // Save the new URL
        cell.leftImageView.kf.setImage(with: resource)
    }
} else {
    print("Cannot form url for brand logo")
}

Я бы предпочел поместить этот код в саму ячейку, хотя

var leftImageURL: URL {
    didSet {
        if(oldValue != leftImageURL) {
            let resource = ImageResource(downloadURL: url, cacheKey: url.absoluteString)
            leftImageView.kf.setImage(with: resource)
        }
    }
}

но это полностью зависит от вас.

0 голосов
/ 27 ноября 2018

Если вы добавляете данные в конец tableView, не вызывайте reloadData, что вызывает пересчет и перерисовку всех ячеек.Вместо этого используйте UITableView.insertRows (at: with:) , который будет выполнять соответствующую анимацию вставки, если вы используете .automatic и оставляете существующие ячейки в покое.

...