Skip to content

Commit 392e3a5

Browse files
committed
adding item moves to collection updates
1 parent b2d13b2 commit 392e3a5

File tree

6 files changed

+55
-56
lines changed

6 files changed

+55
-56
lines changed

Example/Example/CollectionViewController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class CollectionViewController: UIViewController {
3636
[Person(name: "Jim"), Person(name: "Jane")],
3737
]
3838
let second = [
39-
[Person(name: "Pete")],
39+
[Person(name: "Pete"), Person(name: "Vicki")],
4040
[Person(name: "Jim")],
4141
]
4242

Example/Example/GitHubSearchViewController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import UIKit
77
import Combine
88
import CombineDataSources
99

10-
struct Repo: Codable, Equatable {
10+
struct Repo: Codable, Hashable {
1111
let name: String
1212
let description: String?
1313
}

Example/Example/ViewController.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import UIKit
77
import Combine
88
import CombineDataSources
99

10-
struct Person: Equatable {
10+
struct Person: Hashable {
1111
let name: String
1212
}
1313

@@ -28,10 +28,10 @@ class ViewController: UIViewController {
2828
// Test data set to use
2929
let first = [
3030
[Person(name: "Julia"), Person(name: "Vicki"), Person(name: "Pete")],
31-
[Person(name: "Jim"), Person(name: "Jane")],
31+
[Person(name: "Jane"), Person(name: "Jim")],
3232
]
3333
let second = [
34-
[Person(name: "Vicki")],
34+
[Person(name: "Pete"), Person(name: "Vicki")],
3535
[Person(name: "Jim")],
3636
]
3737

Sources/CombineDataSources/CollectionViewItemsController.swift

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import Combine
1111
public class CollectionViewItemsController<CollectionType>: NSObject, UICollectionViewDataSource
1212
where CollectionType: RandomAccessCollection,
1313
CollectionType.Index == Int,
14-
CollectionType.Element: Equatable,
14+
CollectionType.Element: Hashable,
1515
CollectionType.Element: RandomAccessCollection,
1616
CollectionType.Element.Index == Int,
17-
CollectionType.Element.Element: Equatable {
17+
CollectionType.Element.Element: Hashable {
1818

1919
public typealias Element = CollectionType.Element.Element
2020
public typealias CellFactory<Element: Equatable> = (CollectionViewItemsController<CollectionType>, UICollectionView, IndexPath, Element) -> UICollectionViewCell
@@ -63,35 +63,19 @@ public class CollectionViewItemsController<CollectionType>: NSObject, UICollecti
6363
// Commit the changes to the collection view sections
6464
collectionView.performBatchUpdates({[unowned self] in
6565
for sectionIndex in 0..<items.count {
66+
let rowAtIndex = self.fromRow(sectionIndex)
6667
let changes = delta(newList: items[sectionIndex], oldList: collection[sectionIndex])
67-
collectionView.deleteItems(at: changes.removals.map(self.fromRow(sectionIndex)))
68-
collectionView.insertItems(at: changes.insertions.map(self.fromRow(sectionIndex)))
68+
69+
collectionView.deleteItems(at: changes.removals.map(rowAtIndex))
70+
collectionView.insertItems(at: changes.insertions.map(rowAtIndex))
71+
for move in changes.moves {
72+
collectionView.moveItem(at: rowAtIndex(move.0), to: rowAtIndex(move.1))
73+
}
6974
}
7075
collection = items
7176
}, completion: nil)
7277
}
7378

74-
private func delta<T>(newList: T, oldList: T) -> (insertions: [Int], removals: [Int])
75-
where T: RandomAccessCollection, T.Element: Equatable {
76-
77-
let changes = newList.difference(from: oldList)
78-
79-
let insertIndexes = changes.compactMap { change -> Int? in
80-
guard case CollectionDifference<T.Element>.Change.insert(let offset, _, _) = change else {
81-
return nil
82-
}
83-
return offset
84-
}
85-
let deleteIndexes = changes.compactMap { change -> Int? in
86-
guard case CollectionDifference<T.Element>.Change.remove(let offset, _, _) = change else {
87-
return nil
88-
}
89-
return offset
90-
}
91-
92-
return (insertions: insertIndexes, removals: deleteIndexes)
93-
}
94-
9579
// MARK: - UITableViewDataSource protocol
9680
public func numberOfSections(in collectionView: UICollectionView) -> Int {
9781
guard collection != nil else { return 0 }

Sources/CombineDataSources/Section.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public protocol SectionProtocol {
1313
var items: [Element] { get }
1414
}
1515

16-
public struct Section<Element: Equatable>: SectionProtocol, Identifiable {
16+
public struct Section<Element: Hashable>: SectionProtocol, Identifiable {
1717
public init(header: String? = nil, items: [Element], footer: String? = nil, id: String? = nil) {
1818
self.id = id ?? header ?? UUID().uuidString
1919
self.header = header
@@ -34,6 +34,12 @@ extension Section: Equatable {
3434
}
3535
}
3636

37+
extension Section: Hashable {
38+
public func hash(into hasher: inout Hasher) {
39+
hasher.combine(id)
40+
}
41+
}
42+
3743
extension Section: RandomAccessCollection {
3844
public var startIndex: Int {
3945
return items.startIndex

Sources/CombineDataSources/TableViewItemsController.swift

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import Combine
1111
public class TableViewItemsController<CollectionType>: NSObject, UITableViewDataSource
1212
where CollectionType: RandomAccessCollection,
1313
CollectionType.Index == Int,
14-
CollectionType.Element: Equatable,
14+
CollectionType.Element: Hashable,
1515
CollectionType.Element: RandomAccessCollection,
1616
CollectionType.Element.Index == Int,
17-
CollectionType.Element.Element: Equatable {
17+
CollectionType.Element.Element: Hashable {
1818

1919
public typealias Element = CollectionType.Element.Element
2020
public typealias CellFactory<Element: Equatable> = (TableViewItemsController<CollectionType>, UITableView, IndexPath, Element) -> UITableViewCell
@@ -70,35 +70,18 @@ public class TableViewItemsController<CollectionType>: NSObject, UITableViewData
7070
// Commit the changes to the table view sections
7171
tableView.beginUpdates()
7272
for sectionIndex in 0..<items.count {
73+
let rowAtIndex = fromRow(sectionIndex)
7374
let changes = delta(newList: items[sectionIndex], oldList: collection[sectionIndex])
74-
tableView.deleteRows(at: changes.removals.map(fromRow(sectionIndex)), with: rowAnimations.delete)
75-
tableView.insertRows(at: changes.insertions.map(fromRow(sectionIndex)), with: rowAnimations.insert)
75+
tableView.deleteRows(at: changes.removals.map(rowAtIndex), with: rowAnimations.delete)
76+
tableView.insertRows(at: changes.insertions.map(rowAtIndex), with: rowAnimations.insert)
77+
for move in changes.moves {
78+
tableView.moveRow(at: rowAtIndex(move.0), to: rowAtIndex(move.1))
79+
}
7680
}
7781
collection = items
7882
tableView.endUpdates()
7983
}
8084

81-
private func delta<T>(newList: T, oldList: T) -> (insertions: [Int], removals: [Int])
82-
where T: RandomAccessCollection, T.Element: Equatable {
83-
84-
let changes = newList.difference(from: oldList)
85-
86-
let insertIndexes = changes.compactMap { change -> Int? in
87-
guard case CollectionDifference<T.Element>.Change.insert(let offset, _, _) = change else {
88-
return nil
89-
}
90-
return offset
91-
}
92-
let deleteIndexes = changes.compactMap { change -> Int? in
93-
guard case CollectionDifference<T.Element>.Change.remove(let offset, _, _) = change else {
94-
return nil
95-
}
96-
return offset
97-
}
98-
99-
return (insertions: insertIndexes, removals: deleteIndexes)
100-
}
101-
10285
// MARK: - UITableViewDataSource protocol
10386
public func numberOfSections(in tableView: UITableView) -> Int {
10487
guard collection != nil else { return 0 }
@@ -132,3 +115,29 @@ public class TableViewItemsController<CollectionType>: NSObject, UITableViewData
132115
return dataSource
133116
}
134117
}
118+
119+
internal func delta<T>(newList: T, oldList: T) -> (insertions: [Int], removals: [Int], moves: [(Int, Int)])
120+
where T: RandomAccessCollection, T.Element: Hashable {
121+
122+
let changes = newList.difference(from: oldList).inferringMoves()
123+
124+
var insertions = [Int]()
125+
var removals = [Int]()
126+
var moves = [(Int, Int)]()
127+
128+
for change in changes {
129+
switch change {
130+
case .insert(offset: let index, element: _, associatedWith: let associatedIndex):
131+
if let fromIndex = associatedIndex {
132+
moves.append((fromIndex, index))
133+
} else {
134+
insertions.append(index)
135+
}
136+
case .remove(offset: let index, element: _, associatedWith: let associatedIndex):
137+
if associatedIndex == nil {
138+
removals.append(index)
139+
}
140+
}
141+
}
142+
return (insertions: insertions, removals: removals, moves: moves)
143+
}

0 commit comments

Comments
 (0)