123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- //
- // CollectionViewSectionedDataSource.swift
- // RxDataSources
- //
- // Created by Krunoslav Zaher on 7/2/15.
- // Copyright © 2015 Krunoslav Zaher. All rights reserved.
- //
- #if os(iOS) || os(tvOS)
- import Foundation
- import UIKit
- #if !RX_NO_MODULE
- import RxCocoa
- #endif
- import Differentiator
-
- open class CollectionViewSectionedDataSource<Section: SectionModelType>
- : NSObject
- , UICollectionViewDataSource
- , SectionedViewDataSourceType {
- public typealias Item = Section.Item
- public typealias Section = Section
- public typealias ConfigureCell = (CollectionViewSectionedDataSource<Section>, UICollectionView, IndexPath, Item) -> UICollectionViewCell
- public typealias ConfigureSupplementaryView = (CollectionViewSectionedDataSource<Section>, UICollectionView, String, IndexPath) -> UICollectionReusableView
- public typealias MoveItem = (CollectionViewSectionedDataSource<Section>, _ sourceIndexPath:IndexPath, _ destinationIndexPath:IndexPath) -> Void
- public typealias CanMoveItemAtIndexPath = (CollectionViewSectionedDataSource<Section>, IndexPath) -> Bool
- public init(
- configureCell: @escaping ConfigureCell,
- configureSupplementaryView: ConfigureSupplementaryView? = nil,
- moveItem: @escaping MoveItem = { _, _, _ in () },
- canMoveItemAtIndexPath: @escaping CanMoveItemAtIndexPath = { _, _ in false }
- ) {
- self.configureCell = configureCell
- self.configureSupplementaryView = configureSupplementaryView
- self.moveItem = moveItem
- self.canMoveItemAtIndexPath = canMoveItemAtIndexPath
- }
- #if DEBUG
- // If data source has already been bound, then mutating it
- // afterwards isn't something desired.
- // This simulates immutability after binding
- var _dataSourceBound: Bool = false
- private func ensureNotMutatedAfterBinding() {
- assert(!_dataSourceBound, "Data source is already bound. Please write this line before binding call (`bindTo`, `drive`). Data source must first be completely configured, and then bound after that, otherwise there could be runtime bugs, glitches, or partial malfunctions.")
- }
-
- #endif
- // This structure exists because model can be mutable
- // In that case current state value should be preserved.
- // The state that needs to be preserved is ordering of items in section
- // and their relationship with section.
- // If particular item is mutable, that is irrelevant for this logic to function
- // properly.
- public typealias SectionModelSnapshot = SectionModel<Section, Item>
-
- private var _sectionModels: [SectionModelSnapshot] = []
- open var sectionModels: [Section] {
- return _sectionModels.map { Section(original: $0.model, items: $0.items) }
- }
- open subscript(section: Int) -> Section {
- let sectionModel = self._sectionModels[section]
- return Section(original: sectionModel.model, items: sectionModel.items)
- }
-
- open subscript(indexPath: IndexPath) -> Item {
- get {
- return self._sectionModels[indexPath.section].items[indexPath.item]
- }
- set(item) {
- var section = self._sectionModels[indexPath.section]
- section.items[indexPath.item] = item
- self._sectionModels[indexPath.section] = section
- }
- }
-
- open func model(at indexPath: IndexPath) throws -> Any {
- return self[indexPath]
- }
-
- open func setSections(_ sections: [Section]) {
- self._sectionModels = sections.map { SectionModelSnapshot(model: $0, items: $0.items) }
- }
-
- open var configureCell: ConfigureCell {
- didSet {
- #if DEBUG
- ensureNotMutatedAfterBinding()
- #endif
- }
- }
- open var configureSupplementaryView: ConfigureSupplementaryView? {
- didSet {
- #if DEBUG
- ensureNotMutatedAfterBinding()
- #endif
- }
- }
-
- open var moveItem: MoveItem {
- didSet {
- #if DEBUG
- ensureNotMutatedAfterBinding()
- #endif
- }
- }
- open var canMoveItemAtIndexPath: ((CollectionViewSectionedDataSource<Section>, IndexPath) -> Bool)? {
- didSet {
- #if DEBUG
- ensureNotMutatedAfterBinding()
- #endif
- }
- }
- // UICollectionViewDataSource
-
- open func numberOfSections(in collectionView: UICollectionView) -> Int {
- return _sectionModels.count
- }
-
- open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
- return _sectionModels[section].items.count
- }
-
- open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
- precondition(indexPath.item < _sectionModels[indexPath.section].items.count)
-
- return configureCell(self, collectionView, indexPath, self[indexPath])
- }
-
- open func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
- return configureSupplementaryView!(self, collectionView, kind, indexPath)
- }
-
- open func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {
- guard let canMoveItem = canMoveItemAtIndexPath?(self, indexPath) else {
- return false
- }
-
- return canMoveItem
- }
-
- open func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
- self._sectionModels.moveFromSourceIndexPath(sourceIndexPath, destinationIndexPath: destinationIndexPath)
- self.moveItem(self, sourceIndexPath, destinationIndexPath)
- }
- override open func responds(to aSelector: Selector!) -> Bool {
- if aSelector == #selector(UICollectionViewDataSource.collectionView(_:viewForSupplementaryElementOfKind:at:)) {
- return configureSupplementaryView != nil
- }
- else {
- return super.responds(to: aSelector)
- }
- }
- }
- #endif
|