面临 cellForRowAt 中的 indexPathsForVisibleRows 问题

Facing issue with indexPathsForVisibleRows in cellForRowAt

提问人:Madhu 提问时间:10/12/2023 更新时间:10/13/2023 访问量:71

问:

我正在显示来自 url 的图像并用于缓存,它按预期工作。我现在面临的唯一问题是在非常快速地滚动 UITableView 的同时下载图像时,在某些时候滚动时,我检查 visibleRows 的 if 条件失败,因此在下载图像后重新加载特定单元格不会发生,因此单元格显示占位符图像。并非每张图像都会发生这种情况,在快速滚动时仅观察到几张图像的问题,图像 url 没有问题,如果我来回滚动,会再次加载相同的图像。由于在单元格可见之前加载,因此条件失败。如何确保快速滚动时条件不会失败UITableViewSDWebImagecellForRowAt

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCell) as? TableViewCell else {
        // fall back code
    }
    
    //code to get the url from userData object
    let imageUrl = userData.imagePath
    //if the image is not in the cache download it
    guard let _ = SDImageCache.shared.diskImageData(forKey: imageUrl) else {
        if let url = URL(string: imageUrl) {
            SDWebImagePrefetcher.shared.prefetchURLs([url], progress: nil) { [weak self] (_, _) in
                guard let self = self else { return }
                // i am facing using with this if condition sometimes the if condition is returning false
                if let visibleRows = self.tableView.indexPathsForVisibleRows, visibleRows.contains(indexPath) {
                    self.tableView.reloadRows(at: [indexPath], with: .none)
                }
            }
        }
        cell.setNeedsLayout()
        return cell
    }
}

我在里面设置图像,所以我只需要触发 reloadRows 以便显示下载的图像。CustomTableViewCell

注意:中的代码没有问题,问题仅出在里面的 if 条件上,如果我删除 if 条件,那么它就可以正常工作并且能够正确查看所有图像,但重新加载不可见的单元格会影响性能。CustomTableViewCellcellForRowAt

尝试以下解决方案

  • 在主线程中运行代码
  • 添加代码willDisplay cell:

没有任何效果,需要一个解决方案来避免条件失败。任何意见将不胜感激。

iOS Swift UITableView

评论

0赞 Maziar Saadatfar 10/12/2023
为什么不使用 from: cell.imageView.sd_setImage(with: URL(string: url), placeholderImage: UIImage(named: yourPlaceholderImage))
0赞 Maziar Saadatfar 10/12/2023
IMO 您的代码不正确,我认为重新加载 tableviewrow 再次调用索引处的 cellforrow,它会导致循环并在快速操作中显示,它(问题)显示,您可以显示单元格设计吗?
0赞 Madhu 10/12/2023
@MaziarSaadatfar。我不能使用 cell.imageView.sd_setImage(...),因为我已经有一个用 UITableViewCustomCell 编写的旧逻辑来显示占位符,我将无法更改它。仅供参考,您可以将 UI 视为放置在单元格内的普通 ImageView,不会出现重大并发症。正如我之前提到的,图像不是问题,即使在快速滚动时,我也可以看到所有图像只有 1 或 2 没有加载,if 条件失败,所以我需要一个解决方案来修复它。
0赞 Victor Engel 10/12/2023
在我看来,对于快速滚动,您不希望限制为可见行。哪些行是可见的,变化太快。请考虑预取 developer.apple.com/documentation/uikit/... 来填充缓存。然后,也许缓存会在您需要时加载。编辑:我的错误。我看到你已经在预取了。我认为您应该抛弃仅使用可见行的概念。需要时,受托人将询问您的行详细信息。
1赞 Matic Oblak 10/12/2023
您面临的问题可能是您仅包含可见单元格和缺少准备好的单元格。您可以将 if 语句修改为 .仅当单元格可见或已准备好时,此方法才会返回该单元格。试试这个改变。if self.tableView.cellForRow(at: indexPath) != nil

答:

1赞 Matic Oblak 10/13/2023 #1

您面临的问题似乎是,下载映像后,您只会更新可见的单元格,而不会更新准备好的单元格。

对于某些单元格,您遇到的是下载是由方法触发的。然后这个单元格离开屏幕(或保持在屏幕之外),但处于所谓的“准备”状态。下载完成后,您不会更新单元格,因为它不可见。当您滚动到单元格时,它不会按方法更新,因为没有为准备好的单元格调用此方法。tableView:cellForRowAttableView:cellForRowAt

我可以想象这种情况发生的可能性很小,调试起来也很痛苦。

幸运的是,您可以使用方法检测可见细胞和制备的细胞。此方法不会使单元格出列,也不会执行任何其他复杂逻辑。它只是返回一个单元格,该单元格已注册为可见或已准备好,否则为 。tableView.cellForRow(at: indexPath)nil

请尝试以下操作来解决此问题:

if self.tableView.cellForRow(at: indexPath) != nil {
    self.tableView.reloadRows(at: [indexPath], with: .none)
}

评论

0赞 DonMag 10/13/2023
你不需要...如果该 indexPath 的单元格当前不存在,则直接忽略调用。if.reloadRows(at: [indexPath], with: .none)
0赞 Matic Oblak 10/13/2023
@DonMag 你确定吗?根据我的经验,一旦你开始重新加载细胞,很多事情就会发生在后面。例如,如果您使用自动单元格高度,则表视图将使某些单元格高度缓存失效。它可以很容易地显示在异常中,例如滚动条跳转,有时甚至移动内容。
0赞 DonMag 10/13/2023
马蒂奇 - 看看这个:pastebin.com/xVcQH7CD......表格视图上方的按钮...每个按钮点击都会在字符串中添加一行,并调用...如果滚动到该行不在屏幕状态,则可以点击-点击-点击离开,你会看到该行没有被调用。当然,当您向后滚动以使该行可见时,将被调用,并且单元格将重新加载其更新的数据。myData[4]tableView.reloadRows(at: [IndexPath(row: 4, section: 0)], with: .none)cellForRowAtcellForRowAt
0赞 Matic Oblak 10/13/2023
@DonMag我能理解那不是被召唤的。我挑战“电话被简单地忽略了”。整个调用被“忽略”(或至少什么都不做)并不等于不调用其中一个方法。或者换句话说;即使没有调用,也会调用其他东西,这可能会给您带来不便,因此使用该 if 语句仍然有意义。cellForRowAt.reloadRows(at: [indexPath], with: .none)cellForRowAt
0赞 DonMag 10/13/2023
来自 Apple 的文档:“重新加载一行会导致表视图要求其数据源为该行提供新单元格。调用的事实表示它被忽略。我不为 Apple 工作,所以我不能指出一行代码说“如果行不可见 { return }”,但是,我们可以很容易地证明没有任何变化。cellForRowAt