我很难找到NSDiffableDataSourceSnapshot reloadItems(_:) 的使用:

  • 如果我要重新加载的项目与数据源中已经存在的项目不相等,则会崩溃。
  • 但是,如果该项目等于数据源中已经存在的项目,那么“重新加载”它的意义是什么?

  • 您可能会认为,第二点的答案是:好吧,项目标识符对象可能存在其他一些方面,而不是其等同性的一部分,但确实反射(reflect)到单元格接口(interface)中。但是我发现那是不对的。调用reloadItems后,表 View 不反射(reflect)更改。
    因此,当我要更改项目时,最终要对快照执行的操作是要替换的项目之后的insert,然后是原始项目的delete。没有快照replace方法,这就是我希望reloadItems变成的方法。
    (我对这些术语进行了Stack Overflow搜索,却发现很少-大多数只是几个问题,这些问题使reloadItems的特定用途(例如How to update a table cell using diffable UITableView)感到困惑。因此,我以一种更笼统的形式问,有人发现了什么实际用途?对于这种方法?)

    嗯,没有什么比拥有一个可重现的最小示例更好的了,所以这里是一个。
    使用其模板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
        }
    }
    
    接下来,(伪)表 View 和可扩散数据源:
    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)
    }
    
    因此,在我们的可扩散数据源中只有一个UniBool,它的booltrue。因此,现在设置一个按钮来调用此操作方法,该方法尝试使用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仍然为true。
    这就是我要问的问题。如果快照不准备使用bool的新值,那么reloadItems首先是什么?
    显然,我可以用另一种UniBool代替,即用另一种UUID替代。但是后来我不能调用reloadItems;我们崩溃,因为该数据中还没有UniBool。我可以通过先调用insert然后再调用remove来解决此问题,而这正是我解决该问题的方法。
    但是我的问题是:那么reloadItems是做什么的,如果不是因为这件事呢?

    最佳答案

    我同意,根据您的新示例代码,它看起来像个错误。当您将reloadItems添加到快照时,它会正确触发数据源闭包以请求更新的单元格,但是传递给闭包的IdentifierType项是原始的,而不是reloadItems调用提供的新值。
    如果我将UniBool结构更改为一个类,使其成为引用而不是值类型,则事情按预期进行(因为现在只有一个UniBool实例,而不是具有相同标识符的新实例)。
    目前看来,有两种可能的解决方法:

  • IdentifierType使用引用而不是值类型
  • 使用其他后备存储,例如数组,并通过数据源闭包中的indexPath访问它。

  • 我认为这些都不是理想的。
    有趣的是,在将UniBool更改为类之后,我尝试创建一个新的UniBool实例,该实例与现有实例具有相同的uuid并重新加载该实例。代码崩溃,出现异常,指出为重新加载指定了无效的物品标识符;这听起来不对劲;仅hashValue应该很重要,而不是实际的对象引用。原始对象和新对象都具有相同的hashValue,返回的== true

    原始答案reloadItems有效,但是有两个要点:
  • 您必须从数据源的当前snapshot开始,并在其上调用reloadItems。您无法创建新的快照。
  • 除了item之外,您不能仅依赖传递给CellProvider闭包的identifier-它不表示支持模型(数组)中的最新数据。

  • 第2点表示您需要使用提供的indexPathitem.id从模型中获取更新的对象。
    我创建了一个简单的example,它在表格行中显示当前时间;这是数据源结构:
    struct RowData: Hashable {
        var id: UUID = UUID()
        var name: String
        private let possibleColors: [UIColor] = [.yellow,.orange,.cyan]
        var timeStamp = Date()
    
        func hash(into hasher: inout Hasher) {
            hasher.combine(self.id)
        }
    
        static func ==(lhs: RowData, rhs: RowData) -> Bool {
            return lhs.id == rhs.id
        }
    }
    
    请注意,尽管hash函数仅使用id属性,但也有必要重写==,否则当您尝试重新加载该行时,将使用无效的标识符崩溃。
    每秒重新加载随机选择的行。当您运行代码时,您会看到时间在那些随机选择的行上进行了更新。
    这是使用reloadItems的代码:
    self.timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (timer) in
        guard let datasource = self.tableview.dataSource as? UITableViewDiffableDataSource<Section,RowData> else {
            return
        }
        var snapshot = datasource.snapshot()
        var rowIdentifers = Set<RowData>()
        for _ in 0...Int.random(in: 1...self.arrItems.count) {
            let randomIndex = Int.random(in: 0...self.arrItems.count-1)
            self.arrItems[randomIndex].timeStamp = Date()
            rowIdentifers.insert(self.arrItems[randomIndex])
        }
    
        snapshot.reloadItems(Array(rowIdentifers))
        datasource.apply(snapshot)
    }
    

    关于ios - NSDiffableDataSourceSnapshot `reloadItems`是干什么用的?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/64081701/

    10-11 07:16