断言UITableView内部不一致prefetchedCells和indexPathsForPrefetchedCells不同步

问题描述 投票:0回答:4

将 UITableView 与自定义单元格一起使用。在使用 iOS SDK 14 之前,这一切正常。当开始使用 iOS sdk 15 时,UITableView 在滚动时出现错误。

错误是“断言 UITableView 内部不一致 prefetchedCells 和 indexPathsForPrefetchedCells 不同步”

由于这个错误,单元格开始随机消失。

ios uitableview ios15
4个回答
16
投票

此断言表明 UITableView 内部状态已“损坏”。最常见的原因是表视图正在执行更新或重新加载,并且正在回调数据源或委托方法之一,并且您的代码以某种方式导致 UITableView 上出现意外的“重入”(例如,通过执行从此回调内部进行另一个更新/重新加载或手动运行主运行循环)。这会导致 UITableView 进入不一致的内部状态,因为它已经在处理另一个更新。

就我而言,UITableView 和使用 SwiftSoup 将 html 转换为 attributeString 的 NSAttributedString() init 方法之间的交互很糟糕。它在更新 tableViewCell 时被调用,这导致 cellForRowAt 再次被调用,而我的应用程序代码已经在处理 cellForRowAt 调用。之后 tableView 就失去了理智。

我通过在 cellForRowAt 运行之前将 html 转换为 attributeString 来修复此问题,然后 cellForRowAt 使用它。


2
投票

我也面临这个问题。找了好久,找到了这个视频:

苹果WWDC2021

Apple 所做的一些性能改进打破了我使用的嵌套 UITableView。

我将UITableView的

isPrefetchingEnabled
的值更改为
false
。您可以将其应用于特定的 TableView,也可以为整个应用程序更改它。

if #available(iOS 15.0, *) {
    UITableView.appearance().isPrefetchingEnabled = false
}

0
投票

当单元格的完全相同的实例呈现到同一个表中的两个不同行时,可能会发生这种情况。在我看到的情况下,相同的单元格实例作为第一行添加到两个不同的部分。


0
投票

主要问题不在于直接使用html到属性字符串的转换。该案例是关于在表重新加载时冻结主线程的。 人们可以检查甚至空数据转换为属性字符串

public init(data:options:documentAttributes:) throws

持续时间太长了。

因此,为了避免出现问题和冻结,应该为字符串创建缓存并在非主线程上进行转换。

// int-string dictionary cache
private var attributedStringCache = [Int: NSAttributedString]()

// some conversion function
private func attributedString(html string: String) -> NSAttributedString? {
    ...
    return attributedString
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
    let cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCellReuseId", for: indexPath)
    
    let htmlString = someDataSource[indexPath]
    let hashValue = htmlString.hashValue
    var attributedText = attributedStringCache[hashValue]
    if attributedText == nil {

        DispatchQueue.global(qos: .userInteractive).async {
            attributedText = self.attributedString(html: htmlString)

            DispatchQueue.main.async {
                self.attributedStringCache[hashValue] = attributedText

                UIView.performWithoutAnimation() {
                    tableView.reloadRows(at: [indexPath], with: .none)
                }
            }
        }
    }

    cell.textLabel?.attributedText = attributedText
}
© www.soinside.com 2019 - 2024. All rights reserved.