滚动时UITableView跳跃和闪烁

时间:2018-11-27 14:58:17

标签: ios swift uitableview flicker

我有一个COALESCE(A,B),它显示带有图像和一些文本的单元格。数据是按需请求的-我首先请求10行数据,然后再请求10行,依此类推。我在$contracts = $rp->createQueryBuilder('c') ->select('c') ->addSelect('COALESCE(c.updatedAt,c.createdAt) AS HIDDEN orderDate') ->orderBy('orderDate', 'DESC') ->getQuery()->getResult(); 中进行此操作。问题是,当我收到数据并需要更新UITableView时,有时会跳转和/或闪烁。我打电话给tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)。这是代码的一部分:

tableview

像这样返回的单元格高度是恒定的:

reloadData

我正在使用Kingfisher下载和缓存图像。这是来自数据源的更多代码:

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()
                    }

                }

            }
        }
    }

}

如何避免滚动时表格视图的闪烁和跳动?我查看了一些类似的问题,但找不到适合我的案例的解决方案。

2 个答案:

答案 0 :(得分:1)

如果要将数据附加到tableView的末尾,请不要调用reloadData,这将强制重新计算并重新绘制所有单元格。而是使用UITableView.insertRows(at:with:),如果您使用.automatic并执行现有的单元格放置,则它将执行适当的插入动画。

答案 1 :(得分:1)

要消除跳跃问题,您需要将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)
        }
    }
}

但这完全取决于您。