-
Notifications
You must be signed in to change notification settings - Fork 485
/
RxTableViewSectionedAnimatedDataSource.swift
133 lines (124 loc) · 5.96 KB
/
RxTableViewSectionedAnimatedDataSource.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//
// RxTableViewSectionedAnimatedDataSource.swift
// RxExample
//
// Created by Krunoslav Zaher on 6/27/15.
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
//
#if os(iOS) || os(tvOS)
import Foundation
import UIKit
#if !RX_NO_MODULE
import RxSwift
import RxCocoa
#endif
import Differentiator
open class RxTableViewSectionedAnimatedDataSource<Section: AnimatableSectionModelType>
: TableViewSectionedDataSource<Section>
, RxTableViewDataSourceType {
public typealias Element = [Section]
public typealias DecideViewTransition = (TableViewSectionedDataSource<Section>, UITableView, [Changeset<Section>]) -> ViewTransition
/// Animation configuration for data source
public var animationConfiguration: AnimationConfiguration
/// Calculates view transition depending on type of changes
public var decideViewTransition: DecideViewTransition
#if os(iOS)
public init(
animationConfiguration: AnimationConfiguration = AnimationConfiguration(),
decideViewTransition: @escaping DecideViewTransition = { _, _, _ in .animated },
configureCell: @escaping ConfigureCell,
titleForHeaderInSection: @escaping TitleForHeaderInSection = { _, _ in nil },
titleForFooterInSection: @escaping TitleForFooterInSection = { _, _ in nil },
canEditRowAtIndexPath: @escaping CanEditRowAtIndexPath = { _, _ in false },
canMoveRowAtIndexPath: @escaping CanMoveRowAtIndexPath = { _, _ in false },
sectionIndexTitles: @escaping SectionIndexTitles = { _ in nil },
sectionForSectionIndexTitle: @escaping SectionForSectionIndexTitle = { _, _, index in index }
) {
self.animationConfiguration = animationConfiguration
self.decideViewTransition = decideViewTransition
super.init(
configureCell: configureCell,
titleForHeaderInSection: titleForHeaderInSection,
titleForFooterInSection: titleForFooterInSection,
canEditRowAtIndexPath: canEditRowAtIndexPath,
canMoveRowAtIndexPath: canMoveRowAtIndexPath,
sectionIndexTitles: sectionIndexTitles,
sectionForSectionIndexTitle: sectionForSectionIndexTitle
)
}
#else
public init(
animationConfiguration: AnimationConfiguration = AnimationConfiguration(),
decideViewTransition: @escaping DecideViewTransition = { _, _, _ in .animated },
configureCell: @escaping ConfigureCell,
titleForHeaderInSection: @escaping TitleForHeaderInSection = { _, _ in nil },
titleForFooterInSection: @escaping TitleForFooterInSection = { _, _ in nil },
canEditRowAtIndexPath: @escaping CanEditRowAtIndexPath = { _, _ in false },
canMoveRowAtIndexPath: @escaping CanMoveRowAtIndexPath = { _, _ in false }
) {
self.animationConfiguration = animationConfiguration
self.decideViewTransition = decideViewTransition
super.init(
configureCell: configureCell,
titleForHeaderInSection: titleForHeaderInSection,
titleForFooterInSection: titleForFooterInSection,
canEditRowAtIndexPath: canEditRowAtIndexPath,
canMoveRowAtIndexPath: canMoveRowAtIndexPath
)
}
#endif
var dataSet = false
open func tableView(_ tableView: UITableView, observedEvent: Event<Element>) {
Binder(self) { dataSource, newSections in
#if DEBUG
dataSource._dataSourceBound = true
#endif
if !dataSource.dataSet {
dataSource.dataSet = true
dataSource.setSections(newSections)
tableView.reloadData()
}
else {
// if view is not in view hierarchy, performing batch updates will crash the app
if tableView.window == nil {
dataSource.setSections(newSections)
tableView.reloadData()
return
}
let oldSections = dataSource.sectionModels
do {
let differences = try Diff.differencesForSectionedView(initialSections: oldSections, finalSections: newSections)
switch dataSource.decideViewTransition(dataSource, tableView, differences) {
case .animated:
// each difference must be run in a separate 'performBatchUpdates', otherwise it crashes.
// this is a limitation of Diff tool
for difference in differences {
let updateBlock = {
// sections must be set within updateBlock in 'performBatchUpdates'
dataSource.setSections(difference.finalSections)
tableView.batchUpdates(difference, animationConfiguration: dataSource.animationConfiguration)
}
if #available(iOS 11, tvOS 11, *) {
tableView.performBatchUpdates(updateBlock, completion: nil)
} else {
tableView.beginUpdates()
updateBlock()
tableView.endUpdates()
}
}
case .reload:
dataSource.setSections(newSections)
tableView.reloadData()
return
}
}
catch let e {
rxDebugFatalError(e)
dataSource.setSections(newSections)
tableView.reloadData()
}
}
}.on(observedEvent)
}
}
#endif