【问题标题】:UITableViewDiffableDataSouce: Invalid update: invalid number of sectionsUITableViewDiffableDataSouce:无效更新:无效的节数
【发布时间】:2021-03-11 17:52:12
【问题描述】:

我正在尝试应用一个空快照,它使我的应用程序崩溃。我一直在尝试调试它 2 天,但似乎无法找到解决此问题的方法。下面是我正在运行的代码:

//
//  ItemsListDiffableVC.swift
//  FetchRewardsCodingExercise
//
//  Created by Vandan Patel on 11/26/20.
//

import UIKit

final class ItemsListDiffableVC: UIViewController {
    private var tableView: UITableView!
    private var dataSource: ItemDataSource!
    private var groupedItems = [Dictionary<Int, [Item]>.Element]()
    
    var presenter: ItemsListPresentable!
    
    private let cellReusableID = "itemCell"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureTableView()
        presenter.didLoadView()
    }
    
    private func configureTableView() {
        tableView = UITableView()
        tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        tableView.backgroundColor = .systemGroupedBackground
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReusableID)
        view.addSubview(tableView)
    }
    
    private func configureDataSource() {
        dataSource = ItemDataSource(tableView: self.tableView, cellProvider: { (tableView, indexPath, item) -> UITableViewCell? in
            let cell = tableView.dequeueReusableCell(withIdentifier: self.cellReusableID, for: indexPath) as! ItemCell
            cell.configureCell(withTitle: item.name ?? "")
            return cell
        })
    }
}

extension ItemsListDiffableVC: ItemsListViewable {
    func display(groupedItems: [Dictionary<Int, [Item]>.Element]) {
        DispatchQueue.main.async {
            self.configureDataSource()
            self.update(with: groupedItems)
        }
    }
    
    func display(error: String) {
    }
}

extension ItemsListDiffableVC {
    private func update(with groupedItems: [Dictionary<Int, [Item]>.Element]) {
        var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
        dataSource.apply(snapshot, animatingDifferences: true, completion: nil)
    }
}

class Section: Hashable {
    
    let sectionName: String
    let identifier = UUID()
    
    init(sectionName: String) {
        self.sectionName = sectionName
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(identifier)
    }
    
    static func == (lhs: Section, rhs: Section) -> Bool {
        return lhs.identifier == rhs.identifier
    }
}

class ItemDataSource: UITableViewDiffableDataSource<Section, Item> {
    var groupedItems = [Dictionary<Int, [Item]>.Element]()
    
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return groupedItems[section].key.sectionTitle
    }
}

struct Item: Codable, Hashable {
    let id: Int
    let listId: Int
    let name: String?
}

这是我得到的错误:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections. The number of sections contained in the table view after the update (0) must be equal to the number of sections contained in the table view before the update (1), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).'

我不明白的是为什么在更新之前还有一个部分以及如何处理它。

谢谢。

【问题讨论】:

  • 什么是物品?您的数据在哪里?应用空快照的目的是什么?请出示您的真实代码并说明您的真实目标。
  • 另外,[Dictionary&lt;Int, [Item]&gt;.Element] 是一种奇怪的说话方式。
  • 我只是想帮助找出应用程序崩溃的原因。我应用了一个空快照,以便我们可以轻松地看到 1 个错误。即使我正在应用一个空快照,但错误说它甚至在更新之前就有 1 个部分。这就是我想要弄清楚的。 1、为什么我一初始化tableView就变成了1个section? 2.如果是1,怎么变成0?谢谢!
  • 好吧,你没有提供足够的代码来测试这个问题。请提供一个可运行的、可编译的、独立的示例来执行崩溃。
  • 您的代码仍然无法编译。什么是 ItemsListPresentable?什么是 ItemCell?什么是 ItemsListViewable? sectionTitle 属性从何而来?不能编译就不能运行,不能运行就不能崩溃。

标签: ios swift uitableview uitableviewdiffabledatasource


【解决方案1】:
tableView.dataSource = dataSource

我在提供的代码中没有看到任何地方。

【讨论】:

    【解决方案2】:

    我遇到了同样的问题,我所做的是将 animatingDifferences 设置为 false 并且崩溃消失了

    【讨论】:

      【解决方案3】:

      首先,UIDiffableDataSource 不是为引用类型制作的,所以我建议请将您的所有模型类转换为结构。

      【讨论】:

      • 这不准确。只要类型是可哈希的,就可以了。
      • @Drew 最初我也是这么想的。但是在一个演示项目中我遇到了问题,我在苹果反馈助手中提交了错误并查看了他们的回复。 ibb.co/qgpvhCZ 所以我很震惊,所以我决定进一步调查,我检查了苹果提供的所有 diffable 数据源演示,我发现他们的示例都没有使用引用类型。
      • .@CrackIt 这很有趣,因为文档对这个要求只字未提。我还能够仅使用类来获得可区分的数据源。这很有趣,因为文档没有说明该要求。我还能够仅使用类来获取可区分的数据源。
      • 是的@Drew。所以我现在完全糊涂了。一般来说,我现在避免使用引用类型。尽管最近的 WWDC 21 有一些新变化,但您应该检查一下。我们可能以错误的方式使用了整个 diffable 数据源!
      【解决方案4】:

      我怀疑问题在于您没有向我们展示的代码(因为代码显然不完整;您没有声明 Item,而且您似乎在任何地方都没有任何 数据) .但是,以下是对您展示的代码的一些想法:


      这毫无意义:

          var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
          dataSource.apply(snapshot, animatingDifferences: true, completion: nil)
      

      那个快照是空的。因此,没有要更新的数据,也没有要应用的差异。您的函数命名 func update(with groupedItems: 但实际上无论groupedItems 可能传递给您,您都只是将它们扔掉并使用这个空快照。


      此外,在最初填充数据源后,只发生一次,您永远不会再次创建快照。您总是从数据源获取每个后续​​快照,修改并应用它。


      此外,这显示了对什么是可区分数据源的误解:

      class ItemDataSource: UITableViewDiffableDataSource<Section, Item> {
          var groupedItems = [Dictionary<Int, [Item]>.Element]()
      }
      

      您不使用单独的实例变量将数据保存在可区分的数据源中。 diffable 数据源本身持有数据,数据在应用时由快照传递给它。

      基本上,您需要确定这些数据的真实来源,并从那里一致地填充表格视图数据源。通常情况下,diffable 数据源本身是事实的来源;如果您有充分的理由使用“后备存储”,那很好(也许您的数据会从网络异步更新自己?),但不要使用其中的 两个

      【讨论】:

      • 我正在应用一个空快照以便于调试。没有意义的是,为什么应用空快照时它会崩溃。此崩溃特定于部分,我已共享所有相关代码。不过,我不介意分享更多。我注意到的是 tableView 有 1 部分,一旦它被初始化,它就会被 1 关闭,从而导致崩溃。感谢您抽出时间回复。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-07-01
      • 2020-03-04
      • 2014-10-28
      • 1970-01-01
      • 1970-01-01
      • 2013-07-28
      相关资源
      最近更新 更多