forked from wokalski/Diff.swift
-
Notifications
You must be signed in to change notification settings - Fork 74
Open
Description
I have performed the following:
data before:
[
TestData(id: "SECTION_0", rows: [
"S0_R0",
"S0_R1",
"S0_R2",
"S0_R3",
"S0_R4",
"S0_R5"
]),
TestData(id: "SECTION_1", rows: [
"S1_R0",
"S1_R1"
]),
]
data after:
[
TestData(id: "SECTION_1", rows: [
"S1_R0",
"S1_R1"
]),
TestData(id: "SECTION_0", rows: [
"S0_R0",
"S0_R4", // <-
"S0_R1",
"S0_R2",
"S0_R3", // ->
"S0_R5"
]),
]
We move 1 section up,
and we move 1 row up
As a result i see a total mess of rows and sections
What am i doing wrong?
This behavior is easily reproduced by the following code:
import Foundation
import UIKit
struct TestData: CollectionDecorator {
let id: String
let rows: [String]
typealias InnerCollectionType = [String]
var collection: [String] { rows }
}
class TestController2: UIViewController {
let mTableView = UITableView()
var data: [TestData] = [
TestData(id: "SECTION_0", rows: [
"S0_R0",
"S0_R1",
"S0_R2",
"S0_R3",
"S0_R4",
"S0_R5"
]),
TestData(id: "SECTION_1", rows: [
"S1_R0",
"S1_R1"
]),
]
override func viewDidLoad() {
super.viewDidLoad()
addSubview(mTableView)
mTableView.translatesAutoresizingMaskIntoConstraints = false
mTableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
mTableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
mTableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
mTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
mTableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
mTableView.dataSource = self
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
guard let self = self else { return }
self.recursiveShuffle()
}
}
func recursiveShuffle() {
let newData = [
TestData(id: "SECTION_1", rows: [
"S1_R0",
"S1_R1"
]),
TestData(id: "SECTION_0", rows: [
"S0_R0",
"S0_R4", // <-
"S0_R1",
"S0_R2",
"S0_R3", // ->
"S0_R5"
]),
]
updateTable(newData: newData) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
guard let self = self else { return }
let newDataBack = [
TestData(id: "SECTION_0", rows: [
"S0_R0",
"S0_R1", //->
"S0_R2",
"S0_R3",
"S0_R4", //<-
"S0_R5"
]),
TestData(id: "SECTION_1", rows: [
"S1_R0",
"S1_R1"
]),
]
self.updateTable(newData: newDataBack) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
guard let self = self else { return }
self.recursiveShuffle()
}
}
}
}
}
}
extension TestController2: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return data.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let text = data[indexPath.section][indexPath.row]
cell.textLabel?.text = text
if text.hasPrefix("S0") {
cell.backgroundColor = .green
} else {
cell.backgroundColor = .red
}
return cell
}
func updateTable(newData: [TestData], completion: @escaping () -> Void) {
let diff = data.nestedExtendedDiff(to: newData, isEqualSection: { $0.id == $1.id }, isEqualElement: { $0 == $1 })
CATransaction.begin()
CATransaction.setCompletionBlock(completion)
data = newData
mTableView.apply(diff, indexPathTransform: { $0 }, sectionTransform: { $0 })
CATransaction.commit()
}
}
/// Simply need to make something a collection is easy, if it has a collection inside
protocol CollectionDecorator: Collection {
associatedtype InnerCollectionType: Collection
var collection: InnerCollectionType { get }
}
extension CollectionDecorator {
typealias Index = InnerCollectionType.Index
typealias Element = InnerCollectionType.Element
typealias Iterator = InnerCollectionType.Iterator
typealias SubSequence = InnerCollectionType.SubSequence
typealias Indices = InnerCollectionType.Indices
func makeIterator() -> InnerCollectionType.Iterator { collection.makeIterator() }
var underestimatedCount: Int { collection.underestimatedCount }
func withContiguousStorageIfAvailable<R>(_ body: (UnsafeBufferPointer<Element>) throws -> R) rethrows -> R? {
try collection.withContiguousStorageIfAvailable(body)
}
var startIndex: Self.Index { collection.startIndex }
var endIndex: Self.Index { collection.endIndex }
subscript(position: Self.Index) -> Self.Element {
return collection[position]
}
subscript(bounds: Range<Self.Index>) -> Self.SubSequence {
return collection[bounds]
}
var indices: Self.Indices { return collection.indices }
var isEmpty: Bool { return collection.isEmpty }
var count: Int { return collection.count }
func index(_ i: Self.Index, offsetBy distance: Int) -> Self.Index {
return collection.index(i, offsetBy: distance)
}
func index(_ i: Self.Index, offsetBy distance: Int, limitedBy limit: Self.Index) -> Self.Index? {
return collection.index(i, offsetBy: distance, limitedBy: limit)
}
func distance(from start: Self.Index, to end: Self.Index) -> Int {
return collection.distance(from: start, to: end)
}
func index(after i: Self.Index) -> Self.Index {
collection.index(after: i)
}
func formIndex(after i: inout Self.Index) {
collection.formIndex(after: &i)
}
}
Metadata
Metadata
Assignees
Labels
No labels