123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- //
- // TableViewSectionedDataSource.swift
- // RxDataSources
- //
- // Created by Krunoslav Zaher on 6/15/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 TableViewSectionedDataSource<Section: SectionModelType>
- : NSObject
- , UITableViewDataSource
- , SectionedViewDataSourceType {
-
- public typealias Item = Section.Item
- public typealias ConfigureCell = (TableViewSectionedDataSource<Section>, UITableView, IndexPath, Item) -> UITableViewCell
- public typealias TitleForHeaderInSection = (TableViewSectionedDataSource<Section>, Int) -> String?
- public typealias TitleForFooterInSection = (TableViewSectionedDataSource<Section>, Int) -> String?
- public typealias CanEditRowAtIndexPath = (TableViewSectionedDataSource<Section>, IndexPath) -> Bool
- public typealias CanMoveRowAtIndexPath = (TableViewSectionedDataSource<Section>, IndexPath) -> Bool
- #if os(iOS)
- public typealias SectionIndexTitles = (TableViewSectionedDataSource<Section>) -> [String]?
- public typealias SectionForSectionIndexTitle = (TableViewSectionedDataSource<Section>, _ title: String, _ index: Int) -> Int
- #endif
- #if os(iOS)
- public init(
- 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.configureCell = configureCell
- self.titleForHeaderInSection = titleForHeaderInSection
- self.titleForFooterInSection = titleForFooterInSection
- self.canEditRowAtIndexPath = canEditRowAtIndexPath
- self.canMoveRowAtIndexPath = canMoveRowAtIndexPath
- self.sectionIndexTitles = sectionIndexTitles
- self.sectionForSectionIndexTitle = sectionForSectionIndexTitle
- }
- #else
- public init(
- configureCell: @escaping ConfigureCell,
- titleForHeaderInSection: @escaping TitleForHeaderInSection = { _, _ in nil },
- titleForFooterInSection: @escaping TitleForFooterInSection = { _, _ in nil },
- canEditRowAtIndexPath: @escaping CanEditRowAtIndexPath = { _, _ in false },
- canMoveRowAtIndexPath: @escaping CanMoveRowAtIndexPath = { _, _ in false }
- ) {
- self.configureCell = configureCell
- self.titleForHeaderInSection = titleForHeaderInSection
- self.titleForFooterInSection = titleForFooterInSection
- self.canEditRowAtIndexPath = canEditRowAtIndexPath
- self.canMoveRowAtIndexPath = canMoveRowAtIndexPath
- }
- #endif
- #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 titleForHeaderInSection: TitleForHeaderInSection {
- didSet {
- #if DEBUG
- ensureNotMutatedAfterBinding()
- #endif
- }
- }
- open var titleForFooterInSection: TitleForFooterInSection {
- didSet {
- #if DEBUG
- ensureNotMutatedAfterBinding()
- #endif
- }
- }
-
- open var canEditRowAtIndexPath: CanEditRowAtIndexPath {
- didSet {
- #if DEBUG
- ensureNotMutatedAfterBinding()
- #endif
- }
- }
- open var canMoveRowAtIndexPath: CanMoveRowAtIndexPath {
- didSet {
- #if DEBUG
- ensureNotMutatedAfterBinding()
- #endif
- }
- }
- #if os(iOS)
- open var sectionIndexTitles: SectionIndexTitles {
- didSet {
- #if DEBUG
- ensureNotMutatedAfterBinding()
- #endif
- }
- }
- open var sectionForSectionIndexTitle: SectionForSectionIndexTitle {
- didSet {
- #if DEBUG
- ensureNotMutatedAfterBinding()
- #endif
- }
- }
- #endif
-
- // UITableViewDataSource
-
- open func numberOfSections(in tableView: UITableView) -> Int {
- return _sectionModels.count
- }
-
- open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- guard _sectionModels.count > section else { return 0 }
- return _sectionModels[section].items.count
- }
-
- open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- precondition(indexPath.item < _sectionModels[indexPath.section].items.count)
-
- return configureCell(self, tableView, indexPath, self[indexPath])
- }
-
- open func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
- return titleForHeaderInSection(self, section)
- }
-
- open func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
- return titleForFooterInSection(self, section)
- }
-
- open func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
- return canEditRowAtIndexPath(self, indexPath)
- }
-
- open func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
- return canMoveRowAtIndexPath(self, indexPath)
- }
- open func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
- self._sectionModels.moveFromSourceIndexPath(sourceIndexPath, destinationIndexPath: destinationIndexPath)
- }
- #if os(iOS)
- open func sectionIndexTitles(for tableView: UITableView) -> [String]? {
- return sectionIndexTitles(self)
- }
-
- open func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
- return sectionForSectionIndexTitle(self, title, index)
- }
- #endif
- }
- #endif
|