|
25 | 25 | import ReactiveKit |
26 | 26 | import UIKit |
27 | 27 |
|
28 | | -extension UICollectionView { |
29 | | - private struct AssociatedKeys { |
30 | | - static var DataSourceKey = "r_DataSourceKey" |
| 28 | +private func applyRowUnitChangeSet<C: CollectionChangesetType where C.Collection.Index == Int>(changeSet: C, collectionView: UICollectionView, sectionIndex: Int) { |
| 29 | + if changeSet.inserts.count > 0 { |
| 30 | + let indexPaths = changeSet.inserts.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } |
| 31 | + collectionView.insertItemsAtIndexPaths(indexPaths) |
31 | 32 | } |
32 | | -} |
33 | 33 |
|
34 | | -extension StreamType where Event.Element: CollectionChangesetType, Event.Element.Collection.Index == Int { |
35 | | - public func bindTo(collectionView: UICollectionView, animated: Bool = true, proxyDataSource: RKCollectionViewProxyDataSource? = nil, createCell: (NSIndexPath, Event.Element.Collection, UICollectionView) -> UICollectionViewCell) -> Disposable { |
| 34 | + if changeSet.updates.count > 0 { |
| 35 | + let indexPaths = changeSet.updates.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } |
| 36 | + collectionView.reloadItemsAtIndexPaths(indexPaths) |
| 37 | + } |
36 | 38 |
|
37 | | - let dataSource = RKCollectionViewDataSource(stream: self, collectionView: collectionView, animated: animated, proxyDataSource: proxyDataSource, createCell: createCell) |
38 | | - objc_setAssociatedObject(collectionView, &UICollectionView.AssociatedKeys.DataSourceKey, dataSource, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) |
39 | | - |
40 | | - return BlockDisposable { [weak collectionView] in |
41 | | - if let collectionView = collectionView { |
42 | | - objc_setAssociatedObject(collectionView, &UICollectionView.AssociatedKeys.DataSourceKey, nil, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) |
43 | | - } |
44 | | - } |
| 39 | + if changeSet.deletes.count > 0 { |
| 40 | + let indexPaths = changeSet.deletes.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } |
| 41 | + collectionView.deleteItemsAtIndexPaths(indexPaths) |
45 | 42 | } |
46 | 43 | } |
47 | 44 |
|
48 | | -@objc public protocol RKCollectionViewProxyDataSource { |
49 | | - optional func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView |
50 | | - optional func collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool |
51 | | - optional func collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) |
| 45 | +extension StreamType where Element: ArrayConvertible { |
| 46 | + |
| 47 | + public func bindTo(collectionView: UICollectionView, animated: Bool = true, createCell: (NSIndexPath, [Element.Element], UICollectionView) -> UICollectionViewCell) -> Disposable { |
| 48 | + return map { CollectionChangeset.initial($0.toArray()) }.bindTo(collectionView, animated: animated, createCell: createCell) |
| 49 | + } |
52 | 50 | } |
53 | 51 |
|
54 | | -public class RKCollectionViewDataSource<S: StreamType where S.Event.Element: CollectionChangesetType, S.Event.Element.Collection.Index == Int>: NSObject, UICollectionViewDataSource { |
55 | | - |
56 | | - private typealias Collection = S.Event.Element.Collection |
57 | | - |
58 | | - private let stream: S |
59 | | - private var sourceCollection: Collection? = nil |
60 | | - private weak var collectionView: UICollectionView! |
61 | | - private let createCell: (NSIndexPath, Collection, UICollectionView) -> UICollectionViewCell |
62 | | - private weak var proxyDataSource: RKCollectionViewProxyDataSource? |
63 | | - private let animated: Bool |
64 | | - |
65 | | - public init(stream: S, collectionView: UICollectionView, animated: Bool = true, proxyDataSource: RKCollectionViewProxyDataSource?, createCell: (NSIndexPath, Collection, UICollectionView) -> UICollectionViewCell) { |
66 | | - self.collectionView = collectionView |
67 | | - self.createCell = createCell |
68 | | - self.proxyDataSource = proxyDataSource |
69 | | - self.stream = stream |
70 | | - self.animated = animated |
71 | | - super.init() |
72 | | - |
73 | | - collectionView.dataSource = self |
| 52 | +extension StreamType where Element: CollectionChangesetType, Element.Collection.Index == Int, Event.Element == Element { |
| 53 | + |
| 54 | + public func bindTo(collectionView: UICollectionView, animated: Bool = true, createCell: (NSIndexPath, Element.Collection, UICollectionView) -> UICollectionViewCell) -> Disposable { |
| 55 | + |
| 56 | + typealias Collection = Element.Collection |
| 57 | + |
| 58 | + let dataSource = collectionView.rDataSource |
| 59 | + let numberOfItems = Property(0) |
| 60 | + let collection = Property<Collection!>(nil) |
| 61 | + |
| 62 | + dataSource.feed( |
| 63 | + collection, |
| 64 | + to: #selector(UICollectionViewDataSource.collectionView(_:cellForItemAtIndexPath:)), |
| 65 | + map: { (value: Collection!, collectionView: UICollectionView, indexPath: NSIndexPath) -> UICollectionViewCell in |
| 66 | + return createCell(indexPath, value, collectionView) |
| 67 | + }) |
| 68 | + |
| 69 | + dataSource.feed( |
| 70 | + numberOfItems, |
| 71 | + to: #selector(UICollectionViewDataSource.collectionView(_:numberOfItemsInSection:)), |
| 72 | + map: { (value: Int, _: UICollectionView, _: Int) -> Int in value } |
| 73 | + ) |
| 74 | + |
| 75 | + dataSource.feed( |
| 76 | + Property(1), |
| 77 | + to: #selector(UICollectionViewDataSource.numberOfSectionsInCollectionView(_:)), |
| 78 | + map: { (value: Int, _: UICollectionView) -> Int in value } |
| 79 | + ) |
| 80 | + |
74 | 81 | collectionView.reloadData() |
75 | 82 |
|
76 | | - stream.observeNext { [weak self] event in |
77 | | - if let uSelf = self { |
78 | | - let justReload = uSelf.sourceCollection == nil |
79 | | - uSelf.sourceCollection = event.collection |
80 | | - if justReload || !animated { |
81 | | - uSelf.collectionView.reloadData() |
| 83 | + let serialDisposable = SerialDisposable(otherDisposable: nil) |
| 84 | + serialDisposable.otherDisposable = observeNext { [weak collectionView] event in |
| 85 | + ImmediateOnMainExecutionContext { |
| 86 | + guard let collectionView = collectionView else { serialDisposable.dispose(); return } |
| 87 | + let justReload = collection.value == nil |
| 88 | + collection.value = event.collection |
| 89 | + numberOfItems.value = event.collection.count |
| 90 | + if justReload || !animated || event.inserts.count + event.deletes.count + event.updates.count == 0 { |
| 91 | + collectionView.reloadData() |
82 | 92 | } else { |
83 | | - uSelf.collectionView.performBatchUpdates({ |
84 | | - RKCollectionViewDataSource.applyRowUnitChangeSet(event, collectionView: uSelf.collectionView, sectionIndex: 0, dataSource: uSelf.proxyDataSource) |
85 | | - }, completion: nil) |
| 93 | + collectionView.performBatchUpdates({ |
| 94 | + applyRowUnitChangeSet(event, collectionView: collectionView, sectionIndex: 0) |
| 95 | + }, completion: nil) |
86 | 96 | } |
87 | 97 | } |
88 | | - }.disposeIn(rBag) |
89 | | - } |
90 | | - |
91 | | - private class func applyRowUnitChangeSet(changeSet: S.Event.Element, collectionView: UICollectionView, sectionIndex: Int, dataSource: RKCollectionViewProxyDataSource?) { |
92 | | - |
93 | | - if changeSet.inserts.count > 0 { |
94 | | - let indexPaths = changeSet.inserts.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } |
95 | | - collectionView.insertItemsAtIndexPaths(indexPaths) |
96 | | - } |
97 | | - |
98 | | - if changeSet.updates.count > 0 { |
99 | | - let indexPaths = changeSet.updates.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } |
100 | | - collectionView.reloadItemsAtIndexPaths(indexPaths) |
101 | 98 | } |
102 | | - |
103 | | - if changeSet.deletes.count > 0 { |
104 | | - let indexPaths = changeSet.deletes.map { NSIndexPath(forItem: $0, inSection: sectionIndex) } |
105 | | - collectionView.deleteItemsAtIndexPaths(indexPaths) |
106 | | - } |
107 | | - } |
108 | | - |
109 | | - /// MARK - UICollectionViewDataSource |
110 | | - |
111 | | - @objc public func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { |
112 | | - return 1 |
| 99 | + return serialDisposable |
113 | 100 | } |
114 | | - |
115 | | - @objc public func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { |
116 | | - return sourceCollection?.count ?? 0 |
117 | | - } |
118 | | - |
119 | | - @objc public func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { |
120 | | - return createCell(indexPath, sourceCollection!, collectionView) |
121 | | - } |
122 | | - |
123 | | - @objc public func collectionView(collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView { |
124 | | - if let view = proxyDataSource?.collectionView?(collectionView, viewForSupplementaryElementOfKind: kind, atIndexPath: indexPath) { |
125 | | - return view |
126 | | - } else { |
127 | | - fatalError("Dear Sir/Madam, your collection view has asked for a supplementary view of a \(kind) kind. Please provide a proxy data source object in bindTo() method that implements `collectionView(collectionView:viewForSupplementaryElementOfKind:atIndexPath)` method!") |
128 | | - } |
129 | | - } |
130 | | - |
131 | | - @objc public func collectionView(collectionView: UICollectionView, canMoveItemAtIndexPath indexPath: NSIndexPath) -> Bool { |
132 | | - return proxyDataSource?.collectionView?(collectionView, canMoveItemAtIndexPath: indexPath) ?? false |
| 101 | +} |
| 102 | + |
| 103 | +extension UICollectionView { |
| 104 | + |
| 105 | + public var rDelegate: ProtocolProxy { |
| 106 | + return protocolProxyFor(UICollectionViewDelegate.self, setter: NSSelectorFromString("setDelegate:")) |
133 | 107 | } |
134 | | - |
135 | | - @objc public func collectionView(collectionView: UICollectionView, moveItemAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) { |
136 | | - proxyDataSource?.collectionView?(collectionView, moveItemAtIndexPath: sourceIndexPath, toIndexPath: destinationIndexPath) |
| 108 | + |
| 109 | + public var rDataSource: ProtocolProxy { |
| 110 | + return protocolProxyFor(UICollectionViewDataSource.self, setter: NSSelectorFromString("setDataSource:")) |
137 | 111 | } |
138 | 112 | } |
0 commit comments