Cells rendered incorrectly when using `UITableView.reload(using:, with:)` under certain contexts
OliverPearmain opened this issue · comments
Checklist
- This is not a Apple's bug.
- Reviewed the README and documents.
- Searched existing issues for ensure not duplicated.
Given
An original list of three elements.
Component(title: "Shuffle Emojis", subtitle: "Shuffle sectioned Emojis in UICollectionView"),
Component(title: "Header Footer Section", subtitle: "Update header/footer by reload section in UITableView"),
Component(title: "Random", subtitle: "Random diff in UICollectionView")
If the list is updated so that the...
- middle element is updated
- first element is moved to the end
The resulting changeset will look like...
[
Changeset(
data: [
Component(title: "Shuffle Emojis", subtitle: "Shuffle sectioned Emojis in UICollectionView"),
Component(title: "Header Footer Section", subtitle: "XXX Update header/footer by reload section in UITableView"),
Component(title: "Random", subtitle: "Random diff in UICollectionView")
],
elementUpdated: [
[element: 1, section: 0]
]
),
Changeset(
data: [
Component(title: "Header Footer Section", subtitle: "XXX Update header/footer by reload section in UITableView"),
Component(title: "Random", subtitle: "Random diff in UICollectionView"),
Component(title: "Shuffle Emojis", subtitle: "Shuffle sectioned Emojis in UICollectionView")
],
elementMoved: [
(source: [element: 1, section: 0], target: [element: 0, section: 0]),
(source: [element: 2, section: 0], target: [element: 1, section: 0])
]
)
]
When
I apply this changeset
to the UITableView
with reload(using:, with:)
...
Expected Behavior
The cells should animate appropriately and the cells should all be in their new positions with their updated values.
Current Behavior
Cells at index 0 and 1 are identical. Element 0 (originally 1) is not present in the UITableView.
Steps to Reproduce
I've modified the demo app in a fork to demonstrate the issue.
- Checkout https://github.com/OliverPearmain/DifferenceKit/tree/reload-with-move-bug
- Run the demo app
- The changeset will automatically be applied to the initial view contoller (so don't navigate anywhere, just watch the animation and resulting UITableView).
Detailed Description (Include Screenshots)
Please note that the "context" under which this happens is important because an identifcal changeset may not result in the same problem.
For instance, in the example code, if I rename the HomeViewController
's components
stored property to data
then the problem goes away, WTF! The naming of properties should not affect the resulting animation, this is most peculiar. Demo code.
Reproducible Demo Project
https://github.com/OliverPearmain/DifferenceKit/tree/reload-with-move-bug
Environments
- Library version: 1.1.5
- Swift version: Swift 5
- iOS version: 13.3
- Xcode version: 11.3.1
- Devices/Simulators: iPhone 11 - 13.3 Simulator
- CocoaPods/Carthage version: n/a
I checked your demo.
You seem to be using reload(using:with:)
method incorrectly.
DifferenceKit applies the diffs in several stages, so you should set the data that passed to the setData
closure to var components
, like below.
let components = [
Component(title: "Header Footer Section", subtitle: "XXX Update header/footer by reload section in UITableView"),
Component(title: "Random", subtitle: "Random diff in UICollectionView"),
Component(title: "Shuffle Emojis", subtitle: "Shuffle sectioned Emojis in UICollectionView"),
]
let changeset = StagedChangeset(source: self.components, target: components)
tableView.reload(using: changeset, with: .fade) { data in
self.components = data
}
Aha, bingo. 🤦♂ Thanks so much.