-
Notifications
You must be signed in to change notification settings - Fork 497
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
488 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// | ||
// DiffableSectionModel.swift | ||
// | ||
// | ||
// Created by mlch911 on 2023/5/30. | ||
// | ||
|
||
import Foundation | ||
|
||
public struct DiffableSectionModel<Section: Hashable, ItemType: Hashable> { | ||
public var model: Section | ||
public var items: [Item] | ||
|
||
public init(model: Section, items: [Item]) { | ||
self.model = model | ||
self.items = items | ||
} | ||
} | ||
|
||
extension DiffableSectionModel: DiffableSectionModelType { | ||
public typealias Identity = Section | ||
public typealias Item = ItemType | ||
|
||
public var identity: Section { | ||
return model | ||
} | ||
} | ||
|
||
extension DiffableSectionModel | ||
: CustomStringConvertible { | ||
|
||
public var description: String { | ||
return "\(self.model) > \(items)" | ||
} | ||
} | ||
|
||
extension DiffableSectionModel { | ||
public init(original: DiffableSectionModel<Section, Item>, items: [Item]) { | ||
self.model = original.model | ||
self.items = items | ||
} | ||
} | ||
|
||
extension DiffableSectionModel | ||
: Equatable { | ||
|
||
public static func == (lhs: DiffableSectionModel, rhs: DiffableSectionModel) -> Bool { | ||
return lhs.model == rhs.model | ||
&& lhs.items == rhs.items | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// | ||
// DiffableSectionModelType.swift | ||
// | ||
// | ||
// Created by mlch911 on 2023/5/30. | ||
// | ||
|
||
import Foundation | ||
|
||
public protocol DiffableSectionModelType: SectionModelType, Hashable where Item: Hashable {} |
100 changes: 100 additions & 0 deletions
100
Sources/RxDataSources/CollectionViewDiffableDataSource.swift
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// | ||
// CollectionViewDiffableDataSource.swift | ||
// | ||
// | ||
// Created by mlch911 on 2023/5/30. | ||
// | ||
|
||
#if os(iOS) || os(tvOS) | ||
import Foundation | ||
import UIKit | ||
#if !RX_NO_MODULE | ||
import RxSwift | ||
import RxCocoa | ||
#endif | ||
|
||
@available(iOS 13.0, tvOS 13.0, *) | ||
open class CollectionViewDiffableDataSource<Section: DiffableSectionModelType>: | ||
UICollectionViewDiffableDataSource<Section, Section.Item> { | ||
|
||
public enum Error: Swift.Error { | ||
case outOfBounds(indexPath: IndexPath) | ||
} | ||
|
||
public typealias ConfigureCellProvider = (_ dataSource: CollectionViewDiffableDataSource<Section>, _ collectionView: UICollectionView, _ indexPath: IndexPath, Section.Item) -> UICollectionViewCell | ||
public typealias ConfigureSupplementaryViewProvider = (_ dataSource: CollectionViewDiffableDataSource<Section>, _ collectionView: UICollectionView, _ elementKind: String, _ indexPath: IndexPath) -> UICollectionReusableView? | ||
public typealias MoveItemProvider = (_ dataSource: CollectionViewDiffableDataSource<Section>, _ sourceIndexPath: IndexPath, _ destinationIndexPath: IndexPath) -> Void | ||
public typealias CanMoveItemAtIndexPathProvider = (_ dataSource: CollectionViewDiffableDataSource<Section>, _ indexPath: IndexPath) -> Bool | ||
|
||
open var configureSupplementaryView: ConfigureSupplementaryViewProvider? | ||
open var canMoveItemAtIndexPath: CanMoveItemAtIndexPathProvider? | ||
open var moveItem: MoveItemProvider? | ||
|
||
public init( | ||
collectionView: UICollectionView, | ||
configureCell: @escaping ConfigureCellProvider, | ||
configureSupplementaryView: @escaping ConfigureSupplementaryViewProvider = { _, _, _, _ in nil }, | ||
moveItem: @escaping MoveItemProvider = { _, _, _ in () }, | ||
canMoveItemAtIndexPath: @escaping CanMoveItemAtIndexPathProvider = { _, _ in true } | ||
) { | ||
self.moveItem = moveItem | ||
self.canMoveItemAtIndexPath = canMoveItemAtIndexPath | ||
self.configureSupplementaryView = configureSupplementaryView | ||
weak var dataSource: CollectionViewDiffableDataSource<Section>! | ||
super.init(collectionView: collectionView, cellProvider: { | ||
configureCell(dataSource, $0, $1, $2) | ||
}) | ||
self.supplementaryViewProvider = { [unowned self] in | ||
self.configureSupplementaryView?(self, $0, $1, $2) | ||
} | ||
dataSource = self | ||
} | ||
|
||
// MARK: - UICollectionViewDataSource | ||
open override func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool { | ||
canMoveItemAtIndexPath?(self, indexPath) ?? false | ||
} | ||
|
||
open override func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { | ||
self.moveItem?(self, sourceIndexPath, destinationIndexPath) | ||
} | ||
} | ||
|
||
extension CollectionViewDiffableDataSource: SectionedViewDataSourceType { | ||
open func model(at indexPath: IndexPath) throws -> Any { | ||
guard let item = itemIdentifier(for: indexPath) else { throw Error.outOfBounds(indexPath: indexPath) } | ||
return item | ||
} | ||
|
||
open func sectionModel(at index: Int) -> Section? { | ||
if #available(iOS 15.0, tvOS 15.0, *) { | ||
return sectionIdentifier(for: index) | ||
} | ||
let sections = snapshot().sectionIdentifiers | ||
guard index >= 0 && index < sections.count else { return nil } | ||
return sections[index] | ||
} | ||
|
||
open subscript(section: Int) -> Section? { | ||
sectionModel(at: section) | ||
} | ||
|
||
open subscript(indexPath: IndexPath) -> Section.Item? { | ||
itemIdentifier(for: indexPath) | ||
} | ||
} | ||
|
||
public extension CollectionViewDiffableDataSource { | ||
func numberOfSections() -> Int { | ||
snapshot().numberOfSections | ||
} | ||
|
||
func numberOfItems(in section: Int) -> Int { | ||
let snapshot = snapshot() | ||
guard section >= 0, section < snapshot.sectionIdentifiers.count else { return 0 } | ||
return snapshot.numberOfItems(inSection: snapshot.sectionIdentifiers[section]) | ||
} | ||
} | ||
|
||
#endif | ||
|
73 changes: 73 additions & 0 deletions
73
Sources/RxDataSources/RxCollectionViewDiffableDataSource.swift
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// | ||
// RxCollectionViewDiffableDataSource.swift | ||
// | ||
// | ||
// Created by mlch911 on 2023/5/30. | ||
// | ||
|
||
#if os(iOS) || os(tvOS) | ||
import Foundation | ||
import UIKit | ||
#if !RX_NO_MODULE | ||
import RxSwift | ||
import RxCocoa | ||
#endif | ||
|
||
@available(iOS 13.0, tvOS 13.0, *) | ||
open class RxCollectionViewDiffableDataSource<Section: DiffableSectionModelType> | ||
: CollectionViewDiffableDataSource<Section> | ||
, RxCollectionViewDataSourceType { | ||
public typealias Element = [Section] | ||
|
||
open func collectionView(_ collectionView: UICollectionView, observedEvent: RxSwift.Event<[Section]>) { | ||
Binder(self) { dataSource, sections in | ||
var snapshot = NSDiffableDataSourceSnapshot<Section, Section.Item>() | ||
snapshot.appendSections(sections) | ||
sections.forEach { section in | ||
snapshot.appendItems(section.items, toSection: section) | ||
} | ||
dataSource.apply(snapshot, animatingDifferences: false) | ||
}.on(observedEvent) | ||
} | ||
} | ||
|
||
@available(iOS 13.0, tvOS 13.0, *) | ||
open class RxCollectionViewAnimatedDiffableDataSource<Section: DiffableSectionModelType> | ||
: CollectionViewDiffableDataSource<Section> | ||
, RxCollectionViewDataSourceType { | ||
public typealias Element = [Section] | ||
public typealias DecideViewTransition = (CollectionViewDiffableDataSource<Section>, UICollectionView, NSDiffableDataSourceSnapshot<Section, Section.Item>) -> ViewTransition | ||
|
||
/// Calculates view transition depending on type of changes | ||
open var decideViewTransition: DecideViewTransition | ||
|
||
public init( | ||
collectionView: UICollectionView, | ||
decideViewTransition: @escaping DecideViewTransition = { _, _, _ in .animated }, | ||
configureCell: @escaping ConfigureCellProvider, | ||
configureSupplementaryView: @escaping ConfigureSupplementaryViewProvider = { _, _, _, _ in nil }, | ||
moveItem: @escaping MoveItemProvider = { _, _, _ in () }, | ||
canMoveItemAtIndexPath: @escaping CanMoveItemAtIndexPathProvider = { _, _ in true } | ||
) { | ||
self.decideViewTransition = decideViewTransition | ||
super.init(collectionView: collectionView, | ||
configureCell: configureCell, | ||
configureSupplementaryView: configureSupplementaryView, | ||
moveItem: moveItem, | ||
canMoveItemAtIndexPath: canMoveItemAtIndexPath) | ||
} | ||
|
||
open func collectionView(_ collectionView: UICollectionView, observedEvent: RxSwift.Event<[Section]>) { | ||
Binder(self) { dataSource, sections in | ||
var snapshot = NSDiffableDataSourceSnapshot<Section, Section.Item>() | ||
snapshot.appendSections(sections) | ||
sections.forEach { section in | ||
snapshot.appendItems(section.items, toSection: section) | ||
} | ||
let animated = dataSource.decideViewTransition(dataSource, collectionView, snapshot) == .animated | ||
dataSource.apply(snapshot, animatingDifferences: animated) | ||
}.on(observedEvent) | ||
} | ||
} | ||
|
||
#endif |
103 changes: 103 additions & 0 deletions
103
Sources/RxDataSources/RxTableViewDiffableDataSource.swift
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
// | ||
// RxTableViewDiffableDataSource.swift | ||
// | ||
// | ||
// Created by mlch911 on 2023/5/30. | ||
// | ||
|
||
#if os(iOS) || os(tvOS) | ||
import Foundation | ||
import UIKit | ||
#if !RX_NO_MODULE | ||
import RxSwift | ||
import RxCocoa | ||
#endif | ||
|
||
@available(iOS 13.0, tvOS 13.0, *) | ||
open class RxTableViewDiffableDataSource<Section: DiffableSectionModelType> | ||
: TableViewDiffableDataSource<Section> | ||
, RxTableViewDataSourceType { | ||
public typealias Element = [Section] | ||
|
||
open func tableView(_ tableView: UITableView, observedEvent: RxSwift.Event<[Section]>) { | ||
Binder(self) { dataSource, sections in | ||
var snapshot = NSDiffableDataSourceSnapshot<Section, Section.Item>() | ||
snapshot.appendSections(sections) | ||
sections.forEach { section in | ||
snapshot.appendItems(section.items, toSection: section) | ||
} | ||
dataSource.apply(snapshot, animatingDifferences: false) | ||
}.on(observedEvent) | ||
} | ||
} | ||
|
||
@available(iOS 13.0, tvOS 13.0, *) | ||
open class RxTableViewAnimatedDiffableDataSource<Section: DiffableSectionModelType> | ||
: TableViewDiffableDataSource<Section> | ||
, RxTableViewDataSourceType { | ||
public typealias Element = [Section] | ||
public typealias DecideViewTransition = (TableViewDiffableDataSource<Section>, UITableView, NSDiffableDataSourceSnapshot<Section, Section.Item>) -> ViewTransition | ||
|
||
/// Calculates view transition depending on type of changes | ||
public var decideViewTransition: DecideViewTransition | ||
|
||
#if os(iOS) | ||
public init( | ||
tableView: UITableView, | ||
animation: UITableView.RowAnimation = .automatic, | ||
decideViewTransition: @escaping DecideViewTransition = { _, _, _ in .animated }, | ||
configureCell: @escaping ConfigureCell, | ||
titleForHeaderInSectionProvider: @escaping TitleForHeaderInSectionProvider = { _, _ in nil }, | ||
titleForFooterInSectionProvider: @escaping TitleForFooterInSectionProvider = { _, _ in nil }, | ||
canEditRowAtIndexPathProvider: @escaping CanEditRowAtIndexPathProvider = { _, _ in true }, | ||
canMoveRowAtIndexPathProvider: @escaping CanMoveRowAtIndexPathProvider = { _, _ in true }, | ||
sectionIndexTitles: @escaping SectionIndexTitlesProvider = { _ in nil }, | ||
sectionForSectionIndexTitle: @escaping SectionForSectionIndexTitleProvider = { _, _, index in index } | ||
) { | ||
self.decideViewTransition = decideViewTransition | ||
super.init(tableView: tableView, | ||
configureCell: configureCell, | ||
titleForHeaderInSectionProvider: titleForHeaderInSectionProvider, | ||
titleForFooterInSectionProvider: titleForFooterInSectionProvider, | ||
canEditRowAtIndexPathProvider: canEditRowAtIndexPathProvider, | ||
canMoveRowAtIndexPathProvider: canMoveRowAtIndexPathProvider, | ||
sectionIndexTitles: sectionIndexTitles, | ||
sectionForSectionIndexTitle: sectionForSectionIndexTitle) | ||
defaultRowAnimation = animation | ||
} | ||
#else | ||
public init( | ||
tableView: UITableView, | ||
animation: UITableView.RowAnimation = .automatic, | ||
decideViewTransition: @escaping DecideViewTransition = { _, _, _ in .animated }, | ||
configureCell: @escaping ConfigureCell, | ||
titleForHeaderInSectionProvider: @escaping TitleForHeaderInSectionProvider = { _, _ in nil }, | ||
titleForFooterInSectionProvider: @escaping TitleForFooterInSectionProvider = { _, _ in nil }, | ||
canEditRowAtIndexPathProvider: @escaping CanEditRowAtIndexPathProvider = { _, _ in true }, | ||
canMoveRowAtIndexPathProvider: @escaping CanMoveRowAtIndexPathProvider = { _, _ in true } | ||
) { | ||
self.decideViewTransition = decideViewTransition | ||
super.init(tableView: tableView, | ||
configureCell: configureCell, | ||
titleForHeaderInSectionProvider: titleForHeaderInSectionProvider, | ||
titleForFooterInSectionProvider: titleForFooterInSectionProvider, | ||
canEditRowAtIndexPathProvider: canEditRowAtIndexPathProvider, | ||
canMoveRowAtIndexPathProvider: canMoveRowAtIndexPathProvider) | ||
defaultRowAnimation = animation | ||
} | ||
#endif | ||
|
||
open func tableView(_ tableView: UITableView, observedEvent: RxSwift.Event<[Section]>) { | ||
Binder(self) { dataSource, sections in | ||
var snapshot = NSDiffableDataSourceSnapshot<Section, Section.Item>() | ||
snapshot.appendSections(sections) | ||
sections.forEach { section in | ||
snapshot.appendItems(section.items, toSection: section) | ||
} | ||
let animated = dataSource.decideViewTransition(dataSource, tableView, snapshot) == .animated | ||
dataSource.apply(snapshot, animatingDifferences: animated) | ||
}.on(observedEvent) | ||
} | ||
} | ||
|
||
#endif |
Oops, something went wrong.