What is NSDiffableDataSourceSnapshot `reloadItems` for?

前端 未结 3 883
耶瑟儿~
耶瑟儿~ 2021-02-02 15:36

I\'m having difficulty finding the use of NSDiffableDataSourceSnapshot reloadItems(_:):

  • If the item I ask to reload is not equatable to an item that is already p

3条回答
  •  抹茶落季
    2021-02-02 15:58

    Based on your new example code, I agree, it looks like a bug. When you add a reloadItems to a snapshot it correctly triggers the datasource closure to request an updated cell, but the IdentifierType item that is passed to the closure is the original, not the new value that was provided with the reloadItems call.

    If I changed your UniBool struct to a class so that it is a reference rather than a value type, then things worked as expected (since there is now a single instance of a UniBool rather than a new one with the same identifier).

    It seems at the moment there are a couple of possible work-arounds:

    1. Use a reference rather than a value type for the IdentifierType
    2. Use an additional backing store, such as an array, and access it via indexPath in the datasource closure.

    I don't think that either of these are ideal.

    Interestingly, after I changed UniBool to a class, I tried creating a new instance of UniBool that had the same uuid as the existing instance and reloading that; The code crashed with an exception stating Invalid item identifier specified for reload; This doesn't sound right to me; Only the hashValue should matter, not the actual object reference. Both the original and the new objects had the same hashValue and == returned true.


    Original answer

    reloadItems works, but there are two important points:

    1. You must start with the datasource's current snapshot and call reloadItems on that. You can't create a new snapshot.

    2. You can't rely on the item passed to the CellProvider closure for anything other than the identifier - It doesn't represent the most recent data from your backing model (array).

    Point 2 means that you need to use the provided indexPath or item.id to obtain your updated object from your model.

    I created a simple example that displays the current time in a table row; This is the data source struct:

    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
        }
    }
    

    Note that despite the hash function only using the id property it is also necessary to override == or you will get a crash with an invalid identifier when you attempt to reload the row.

    Each second a random selection of rows are reloaded. When you run the code you see that the time is updated on those randomly selected rows.

    This is the code that uses reloadItems:

    self.timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (timer) in
        guard let datasource = self.tableview.dataSource as? UITableViewDiffableDataSource else {
            return
        }
        var snapshot = datasource.snapshot()
        var rowIdentifers = Set()
        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)
    }
    

提交回复
热议问题