Last active
April 11, 2017 05:25
-
-
Save cmc5788/7e68ebb9ae853b4fc58c286798d2b96d to your computer and use it in GitHub Desktop.
rxy collection view
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
import RxSwift | |
class RxCollectionView<Element : Comparable>: UICollectionView, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { | |
public typealias E = Element | |
var elements : [E] = [] | |
public var cellSizeChooser : (RxCollectionView<E>, E, Int) -> CGSize = { cv, _, _ in | |
let dim = cv.bounds.size.width / 3.0 | |
return CGSize(width: dim, height: dim) | |
} | |
public var reuseIdentifierFactory : (RxCollectionView<E>, E, Int) -> String = { _, _, _ in "RxCollectionViewCell" } | |
public var cellBinder : (E, UICollectionViewCell, Int) -> Void = { e, c, _ in | |
let l : UILabel = c.viewWithTag(1) as? UILabel ?? UILabel() | |
if l.tag == 0 { | |
l.tag = 1 | |
c.addSubview(l) | |
l.snp.makeConstraints { make in | |
make.width.height.equalToSuperview().offset(-16) | |
make.center.equalToSuperview() | |
} | |
l.lineBreakMode = .byWordWrapping | |
l.numberOfLines = 0 | |
} | |
l.text = "\(e)" | |
} | |
init() { | |
super.init(frame: CGRect(), collectionViewLayout: UICollectionViewFlowLayout()) | |
_init() | |
} | |
override init(frame: CGRect, collectionViewLayout: UICollectionViewLayout) { | |
super.init(frame: frame, collectionViewLayout: collectionViewLayout) | |
_init() | |
} | |
required public init?(coder aDecoder: NSCoder) { | |
super.init(coder: aDecoder) | |
_init() | |
} | |
private func _init() { | |
self.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "RxCollectionViewCell") | |
self.delegate = self | |
self.dataSource = self | |
} | |
deinit { | |
self.delegate = nil | |
self.dataSource = nil | |
} | |
private func deleteElements(_ e : [E]) { | |
if e.count > 1 { print("delete got a batch > 1 !!!!!!!") } | |
var indexPaths : [IndexPath] = [] | |
var filteredElements = self.elements | |
e.forEach { e in | |
if let i = self.elements.index(of: e) { | |
filteredElements = filteredElements.filter { $0 != e } | |
indexPaths.append(IndexPath(row: i, section: 0)) | |
} | |
} | |
if indexPaths.isEmpty { return } | |
self.elements = filteredElements.sorted() | |
self.deleteItems(at: indexPaths) | |
} | |
private func updateOrAddElements(_ e : [E]) { | |
if e.count > 1 { print("append got a batch > 1 !!!!!!!") } | |
var updatingRows : [IndexPath] = [] | |
let `e` : [E] = e.filter { | |
if let i = self.elements.index(of: $0) { | |
updatingRows.append(IndexPath(row: i, section: 0)) | |
self.elements[i] = $0 | |
return false | |
} | |
return true | |
} | |
if !updatingRows.isEmpty { self.reloadItems(at: updatingRows) } | |
if e.isEmpty { return } | |
self.elements.append(contentsOf: e) | |
self.elements.sort() | |
var elementNotFound = false | |
var addingRows : [IndexPath] = [] | |
e.forEach { | |
if let i = self.elements.index(of: $0) { | |
addingRows.append(IndexPath(row: i, section: 0)) | |
} else { | |
print("addElements elementNotFound: SHOULD NEVER HAPPEN") | |
elementNotFound = true | |
} | |
} | |
if elementNotFound || addingRows.isEmpty { | |
print("addElements falling back to reloadSections: SHOULD NEVER HAPPEN") | |
self.reloadSections([0]) | |
} else { | |
self.insertItems(at: addingRows) | |
} | |
} | |
public func bindAdd(_ source : Observable<E>, bufferTimeSpan : RxTimeInterval = 0.1) -> Disposable { | |
return source | |
.buffer(timeSpan: bufferTimeSpan, count: Int.max, scheduler: Schedulers.main) | |
.filter { !$0.isEmpty } | |
.observeOn(Schedulers.main) | |
.do(onNext: self.updateOrAddElements) | |
.subscribe() | |
} | |
public func bindDelete(_ source : Observable<E>, bufferTimeSpan : RxTimeInterval = 0.1) -> Disposable { | |
return source | |
.buffer(timeSpan: bufferTimeSpan, count: Int.max, scheduler: Schedulers.main) | |
.filter { !$0.isEmpty } | |
.observeOn(Schedulers.main) | |
.do(onNext: self.deleteElements) | |
.subscribe() | |
} | |
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { | |
let row = indexPath.row | |
let e = elements[row] | |
return cellSizeChooser(self, e, row) | |
} | |
func numberOfSections(in collectionView: UICollectionView) -> Int { | |
return 1 | |
} | |
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { | |
return 0.0 | |
} | |
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { | |
return 0.0 | |
} | |
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { | |
return UIEdgeInsets.zero | |
} | |
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { | |
return elements.count | |
} | |
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { | |
let row = indexPath.row | |
let e = elements[row] | |
let reuseIdentifier = reuseIdentifierFactory(self, e, row) | |
let cell = dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) | |
cellBinder(e, cell, row) | |
return cell | |
} | |
} | |
extension ObservableType where Self.E : Comparable { | |
func bindCollectionViewAdd<T>(_ collectionView : RxCollectionView<T>, bufferTimeSpan : RxTimeInterval = 0.1) -> Disposable { | |
return collectionView.bindAdd(self as! Observable<T>, bufferTimeSpan: bufferTimeSpan) | |
} | |
func bindCollectionViewDelete<T>(_ collectionView : RxCollectionView<T>, bufferTimeSpan : RxTimeInterval = 0.1) -> Disposable { | |
return collectionView.bindDelete(self as! Observable<T>, bufferTimeSpan: bufferTimeSpan) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment