NSDiffableDataSourceSnapshot `reloadItems` 的用途是什么?

2024-04-05

我很难找到 NSDiffableDataSourceSnapshot 的用途reloadItems(_:) https://developer.apple.com/documentation/uikit/nsdiffabledatasourcesnapshot/3375783-reloaditems:

  • 如果我要求重新加载的项目与数据源中已存在的项目不同,我会崩溃:

    由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“尝试重新加载快照中不存在的项目标识符:ProjectName.ClassName”

  • 但如果该项目is相当于数据源中已经存在的项目,那么“重新加载”它有什么意义呢?

您可能认为第二点的答案是:嗯,项目标识符对象可能还有其他一些方面,它不是其等同性的一部分,但确实反映到单元格接口中。但我发现事实并非如此。打电话后reloadItems,表视图确实not反映变化。

因此,当我想要更改某个项目时,我最终会对快照执行以下操作:insert在要更换的项目之后,然后是delete原始项目的。没有快照replace方法,这正是我所希望的reloadItems事实证明是这样的。

(我对这些术语进行了 Stack Overflow 搜索,但发现很少——主要是几个对特定用途感到困惑的问题reloadItems, 例如如何使用 diffable UITableView 更新表格单元格 https://stackoverflow.com/questions/60180412/how-to-update-a-table-cell-using-diffable-uitableview。所以我以更普遍的形式问,实际用途有什么anyone找到这个方法了吗?)


好吧,没有什么比拥有一个最小的可重现示例更好的了,所以这里就是一个。

使用模板 ViewController 创建一个普通的 iOS 项目,并将此代码添加到 ViewController 中。

我会一点一点地拿走它。首先,我们有一个结构体作为我们的项目标识符。 UUID 是唯一的部分,因此可等性和可散列性仅取决于它:

struct UniBool : Hashable {
    let uuid : UUID
    var bool : Bool
    // equatability and hashability agree, only the UUID matters
    func hash(into hasher: inout Hasher) {
        hasher.combine(uuid)
    }
    static func ==(lhs:Self, rhs:Self) -> Bool {
        lhs.uuid == rhs.uuid
    }
}

接下来,(假)表视图和可比较数据源:

let tableView = UITableView(frame: .zero, style: .plain)
var datasource : UITableViewDiffableDataSource<String,UniBool>!
override func viewDidLoad() {
    super.viewDidLoad()
    self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
    self.datasource = UITableViewDiffableDataSource<String,UniBool>(tableView: self.tableView) { tv, ip, isOn in
        let cell = tv.dequeueReusableCell(withIdentifier: "cell", for: ip)
        return cell
    }
    var snap = NSDiffableDataSourceSnapshot<String,UniBool>()
    snap.appendSections(["Dummy"])
    snap.appendItems([UniBool(uuid: UUID(), bool: true)])
    self.datasource.apply(snap, animatingDifferences: false)
}

所以我们的 diffable 数据源中只有一个 UniBool 及其bool is true。现在设置一个按钮来调用此操作方法,该方法尝试切换bool使用价值reloadItems:

@IBAction func testReload() {
    if let unibool = self.datasource.itemIdentifier(for: IndexPath(row: 0, section: 0)) {
        var snap = self.datasource.snapshot()
        var unibool = unibool
        unibool.bool = !unibool.bool
        snap.reloadItems([unibool]) // this is the key line I'm trying to test!
        print("this object's isOn is", unibool.bool)
        print("but looking right at the snapshot, isOn is", snap.itemIdentifiers[0].bool)
        delay(0.3) {
            self.datasource.apply(snap, animatingDifferences: false)
        }
    }
}

所以事情是这样的。我说reloadItems与 UUID 匹配的项目,但其bool被切换:“该对象的 isON 为 false”。但当我问快照时,好吧,你有什么?它告诉我它唯一的项目标识符bool 仍然是这样.

And that这就是我要问的。如果快照不会获取新值bool, 什么是reloadItems首先?

显然我可以替换不同的UniBool,即具有不同 UUID 的一个。但后来我无法打电话reloadItems;我们崩溃是因为 UniBool 尚未在数据中。我可以通过打电话解决这个问题insert其次是remove,这正是我解决这个问题的方法。

但我的问题是:那么什么是reloadItems因为,如果不是因为这件事呢?


(我已经针对问题中演示的行为提出了错误,因为我认为这不是好的行为。但是,就目前情况而言,我认为我可以猜测这个想法的意图。)


当你告诉快照reload某个项目,它确实not读入您提供的物品的数据!它只是简单地looks在该项目上,作为识别的一种方式what item, already在数据源中,您要求重新加载。

(因此,如果您提供的项目与数据源中已有的项目相同但并非 100% 相同,则您提供的项目与数据源中已有的项目之间的“差异”将一点也不重要;数据源永远不会被告知有什么不同。)

当你那时apply该快照到数据源,数据源告诉表视图重新加载相应的单元格。这导致数据源的小区提供者功能再次被召唤。

好的,现在调用数据源的单元格提供程序函数,并使用通常的三个参数:表视图、索引路径和来自数据源的数据。但是我们刚才说了,来自数据源的数据没有改变。那么重新加载有什么意义呢?

显然,答案是,蜂窝提供商功能预计将看起来别处获取(至少部分)要在新出队的单元格中显示的新数据。您应该拥有手机提供商所关注的某种“后备存储”。例如,您可能正在维护一个字典,其中键是单元标识符类型,值是可能重新加载的额外信息。

这必须是合法的,因为根据定义,单元标识符类型是可哈希的,因此可以用作字典键,而且单元标识符在数据中必须是唯一的,否则数据源将拒绝数据(通过崩溃)。而且查找将是即时的,因为这是一本字典。


这是一个完整的工作示例,您可以直接复制并粘贴到项目中。该表描绘了三个名字以及一个星星,用户可以点击星星来填充或空出星星,以指示最喜欢或不最喜欢的。名称存储在可比较数据源中,但收藏夹状态存储在外部后备存储中。

extension UIResponder {
    func next<T:UIResponder>(ofType: T.Type) -> T? {
        let r = self.next
        if let r = r as? T ?? r?.next(ofType: T.self) {
            return r
        } else {
            return nil
        }
    }
}
class TableViewController: UITableViewController {
    var backingStore = [String:Bool]()
    var datasource : UITableViewDiffableDataSource<String,String>!
    override func viewDidLoad() {
        super.viewDidLoad()
        let cellID = "cell"
        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellID)
        self.datasource = UITableViewDiffableDataSource<String,String>(tableView:self.tableView) {
            tableView, indexPath, name in
            let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath)
            var config = cell.defaultContentConfiguration()
            config.text = name
            cell.contentConfiguration = config
            var accImageView = cell.accessoryView as? UIImageView
            if accImageView == nil {
                let iv = UIImageView()
                iv.isUserInteractionEnabled = true
                let tap = UITapGestureRecognizer(target: self, action: #selector(self.starTapped))
                iv.addGestureRecognizer(tap)
                cell.accessoryView = iv
                accImageView = iv
            }
            let starred = self.backingStore[name, default:false]
            accImageView?.image = UIImage(systemName: starred ? "star.fill" : "star")
            accImageView?.sizeToFit()
            return cell
        }
        var snap = NSDiffableDataSourceSnapshot<String,String>()
        snap.appendSections(["Dummy"])
        let names = ["Manny", "Moe", "Jack"]
        snap.appendItems(names)
        self.datasource.apply(snap, animatingDifferences: false)
        names.forEach {
            self.backingStore[$0] = false
        }
    }
    @objc func starTapped(_ gr:UIGestureRecognizer) {
        guard let cell = gr.view?.next(ofType: UITableViewCell.self) else {return}
        guard let ip = self.tableView.indexPath(for: cell) else {return}
        guard let name = self.datasource.itemIdentifier(for: ip) else {return}
        guard let isFavorite = self.backingStore[name] else {return}
        self.backingStore[name] = !isFavorite
        var snap = self.datasource.snapshot()
        snap.reloadItems([name])
        self.datasource.apply(snap, animatingDifferences: false)
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

NSDiffableDataSourceSnapshot `reloadItems` 的用途是什么? 的相关文章

随机推荐