Skip to content
This repository was archived by the owner on May 14, 2024. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add basic support for UICollectionView
No layout, registration is done every dequeue, many other issues :)
  • Loading branch information
JosephDuffy committed May 24, 2019
commit 43dca6198f76a14fe4b8d6f041c438aa58cda0b8
24 changes: 24 additions & 0 deletions Composed.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@
D941140F2297F4FA00077F90 /* ArraySection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D941140E2297F4FA00077F90 /* ArraySection.swift */; };
D94114112297F86100077F90 /* SectionProviderMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D94114102297F86100077F90 /* SectionProviderMapper.swift */; };
D9D7DCBD22981094001DA4D9 /* SectionProviderMapper+Spec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9D7DCBC22981094001DA4D9 /* SectionProviderMapper+Spec.swift */; };
D9D7DCBF22985C44001DA4D9 /* CollectionViewSectionProviderCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9D7DCBE22985C44001DA4D9 /* CollectionViewSectionProviderCoordinator.swift */; };
D9D7DCC122985F04001DA4D9 /* SectionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9D7DCC022985F04001DA4D9 /* SectionsViewController.swift */; };
D9D7DCC422986949001DA4D9 /* SectionCollectionUIConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9D7DCC322986949001DA4D9 /* SectionCollectionUIConfiguration.swift */; };
D9D7DCC62298695E001DA4D9 /* CollectionUIConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9D7DCC52298695E001DA4D9 /* CollectionUIConfiguration.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -219,6 +223,10 @@
D941140E2297F4FA00077F90 /* ArraySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArraySection.swift; sourceTree = "<group>"; };
D94114102297F86100077F90 /* SectionProviderMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionProviderMapper.swift; sourceTree = "<group>"; };
D9D7DCBC22981094001DA4D9 /* SectionProviderMapper+Spec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SectionProviderMapper+Spec.swift"; sourceTree = "<group>"; };
D9D7DCBE22985C44001DA4D9 /* CollectionViewSectionProviderCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewSectionProviderCoordinator.swift; sourceTree = "<group>"; };
D9D7DCC022985F04001DA4D9 /* SectionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionsViewController.swift; sourceTree = "<group>"; };
D9D7DCC322986949001DA4D9 /* SectionCollectionUIConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionCollectionUIConfiguration.swift; sourceTree = "<group>"; };
D9D7DCC52298695E001DA4D9 /* CollectionUIConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionUIConfiguration.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -275,6 +283,7 @@
isa = PBXGroup;
children = (
540221222195C44F00F0E173 /* AppDelegate.swift */,
D9D7DCC022985F04001DA4D9 /* SectionsViewController.swift */,
54BAB15D221497FA0064CE51 /* GlobalHeaderView.swift */,
54BAB15F221498000064CE51 /* GlobalHeaderView.xib */,
54BAB1632214D05C0064CE51 /* GlobalFooterView.swift */,
Expand Down Expand Up @@ -440,10 +449,21 @@
D941140C2297F40500077F90 /* ComposedSectionProvider.swift */,
D941140E2297F4FA00077F90 /* ArraySection.swift */,
D94114102297F86100077F90 /* SectionProviderMapper.swift */,
D9D7DCBE22985C44001DA4D9 /* CollectionViewSectionProviderCoordinator.swift */,
D9D7DCC222986942001DA4D9 /* UI */,
);
path = Sections;
sourceTree = "<group>";
};
D9D7DCC222986942001DA4D9 /* UI */ = {
isa = PBXGroup;
children = (
D9D7DCC52298695E001DA4D9 /* CollectionUIConfiguration.swift */,
D9D7DCC322986949001DA4D9 /* SectionCollectionUIConfiguration.swift */,
);
path = UI;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXHeadersBuildPhase section */
Expand Down Expand Up @@ -598,6 +618,7 @@
54BAB15E221497FA0064CE51 /* GlobalHeaderView.swift in Sources */,
54BAB1642214D05C0064CE51 /* GlobalFooterView.swift in Sources */,
548A8B702214FA2800C0A276 /* BackgroundView.swift in Sources */,
D9D7DCC122985F04001DA4D9 /* SectionsViewController.swift in Sources */,
540221232195C45000F0E173 /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -626,13 +647,15 @@
54A17324225CC72900A7D7FD /* AggregateDataSource.swift in Sources */,
54A17334225CC72900A7D7FD /* FlowLayout+Metrics.swift in Sources */,
54A17330225CC72900A7D7FD /* FlowLayoutInvalidationContext.swift in Sources */,
D9D7DCBF22985C44001DA4D9 /* CollectionViewSectionProviderCoordinator.swift in Sources */,
54A17325225CC72900A7D7FD /* CollectionDataSource.swift in Sources */,
54A17331225CC72900A7D7FD /* FlowLayout+Helpers.swift in Sources */,
54A17322225CC72900A7D7FD /* DataStore.swift in Sources */,
54A1731E225CC72900A7D7FD /* BasicDataSource.swift in Sources */,
54A1732A225CC72900A7D7FD /* CollectionUIProvidingDataSource.swift in Sources */,
54A1732F225CC72900A7D7FD /* NibLoadable.swift in Sources */,
54A17338225CC72900A7D7FD /* Snap.swift in Sources */,
D9D7DCC62298695E001DA4D9 /* CollectionUIConfiguration.swift in Sources */,
54A1731C225CC72900A7D7FD /* SegmentedDataSource.swift in Sources */,
D941140F2297F4FA00077F90 /* ArraySection.swift in Sources */,
54A17339225CC72900A7D7FD /* DataSourceViewController.swift in Sources */,
Expand All @@ -647,6 +670,7 @@
54A17337225CC72900A7D7FD /* DataSourceHeaderFooterView.swift in Sources */,
54A1731F225CC72900A7D7FD /* ComposedDataSource.swift in Sources */,
54A1733B225CC72900A7D7FD /* CollectionUIViewProvider.swift in Sources */,
D9D7DCC422986949001DA4D9 /* SectionCollectionUIConfiguration.swift in Sources */,
54A1732E225CC72900A7D7FD /* CollectionUISizingStrategy.swift in Sources */,
54A1732B225CC72900A7D7FD /* DataSourceChangeSet.swift in Sources */,
54A17320225CC72900A7D7FD /* ArrayDataStore.swift in Sources */,
Expand Down
8 changes: 6 additions & 2 deletions Composed/Core/Sections/ArraySection.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import Foundation

public final class ArraySection<Element>: MutableSection {
open class ArraySection<Element>: MutableSection {

public weak var updateDelegate: SectionUpdateDelegate?

public var elements: [Element] = []
public var elements: [Element]

public init(elements: [Element] = []) {
self.elements = elements
}

public func element(at index: Int) -> Element {
return elements[index]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import UIKit

public final class CollectionViewSectionProviderCoordinator: NSObject, UICollectionViewDataSource, SectionProviderMapperDelegate {

private let mapper: SectionProviderMapper
private let collectionView: UICollectionView

public init(collectionView: UICollectionView, sectionProvider: SectionProvider) {
self.collectionView = collectionView
mapper = SectionProviderMapper(globalProvider: sectionProvider)

super.init()

collectionView.dataSource = self
}

// MARK: - SectionProviderMapperDelegate

public func sectionProviderMapper(_ sectionProviderMapper: SectionProviderMapper, didInsertSections sections: IndexSet) {
collectionView.insertSections(sections)
}

public func sectionProviderMapper(_ sectionProviderMapper: SectionProviderMapper, didInsertElementsAt indexPaths: [IndexPath]) {
collectionView.insertItems(at: indexPaths)
}

// MARK: - UICollectionViewDataSource

public func numberOfSections(in collectionView: UICollectionView) -> Int {
return mapper.numberOfSections
}

public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return collectionUIConfigurationProvider(for: section)?.numberOfElements ?? 0
}

public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let configuration = collectionUIConfigurationProvider(for: indexPath.section) else {
fatalError("No UI configuration available for section \(indexPath.section)")
}

let type = Swift.type(of: configuration.prototype)
switch configuration.dequeueMethod {
case .nib:
let nib = UINib(nibName: String(describing: type), bundle: Bundle(for: type))
collectionView.register(nib, forCellWithReuseIdentifier: configuration.reuseIdentifier)
case .class:
collectionView.register(type, forCellWithReuseIdentifier: configuration.reuseIdentifier)
}

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: configuration.reuseIdentifier, for: indexPath)
configuration.configure(cell: cell, at: indexPath.row)
return cell
}

private func collectionUIConfigurationProvider(for section: Int) -> CollectionUIConfiguration? {
return (mapper.globalProvider.sections[section] as? CollectionUIConfigurationProvider)?.collectionUIConfiguration
}

}
6 changes: 4 additions & 2 deletions Composed/Core/Sections/ComposedSectionProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ public final class ComposedSectionProvider: AggregateSectionProvider, SectionPro
})
}

public init() { }

public func numberOfElements(in section: Int) -> Int {
return sections[section].numberOfElements
}
Expand Down Expand Up @@ -72,7 +74,7 @@ public final class ComposedSectionProvider: AggregateSectionProvider, SectionPro
return -1
}

func append(_ child: SectionProvider) {
public func append(_ child: SectionProvider) {
child.updateDelegate = self

let firstIndex = sections.count
Expand All @@ -82,7 +84,7 @@ public final class ComposedSectionProvider: AggregateSectionProvider, SectionPro
updateDelegate?.provider(self, didInsertSections: child.sections, at: IndexSet(integersIn: firstIndex..<endIndex))
}

func append(_ child: Section) {
public func append(_ child: Section) {
let index = children.count
children.append(.section(child))
updateDelegate?.provider(self, didInsertSections: [child], at: IndexSet(integer: index))
Expand Down
24 changes: 24 additions & 0 deletions Composed/Core/Sections/UI/CollectionUIConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import UIKit

public protocol CollectionUIConfiguration {
var headerConfiguration: CollectionUIViewProvider? { get }
var footerConfiguration: CollectionUIViewProvider? { get }
var backgroundViewClass: UICollectionReusableView.Type? { get }
var numberOfElements: Int { get }
var reuseIdentifier: String { get }
var prototype: UICollectionReusableView { get }
var dequeueMethod: DequeueMethod { get }

func configure(cell: UICollectionViewCell, at index: Int)
}

public enum DequeueMethod {
/// Load from a nib
case nib
/// Load from a class
case `class`
}

public protocol CollectionUIConfigurationProvider {
var collectionUIConfiguration: CollectionUIConfiguration { get }
}
58 changes: 58 additions & 0 deletions Composed/Core/Sections/UI/SectionCollectionUIConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import UIKit

open class SectionCollectionUIConfiguration: CollectionUIConfiguration {

public let headerConfiguration: CollectionUIViewProvider?

public let footerConfiguration: CollectionUIViewProvider?

public let backgroundViewClass: UICollectionReusableView.Type?

open var numberOfElements: Int {
return section?.numberOfElements ?? 0
}

public private(set) lazy var reuseIdentifier: String = {
return prototype.reuseIdentifier ?? type(of: prototype).reuseIdentifier
}()

public let dequeueMethod: DequeueMethod

private let prototypeProvider: () -> UICollectionReusableView
private var _prototypeView: UICollectionReusableView?

public var prototype: UICollectionReusableView {
if let view = _prototypeView { return view }
let view = prototypeProvider()
_prototypeView = view
return view
}

private weak var section: Section?
private let configureCell: (UICollectionViewCell, Int) -> Void

public init<Cell: UICollectionViewCell, Section: Composed.Section>(section: Section, prototype: @escaping @autoclosure () -> Cell, cellDequeueMethod: DequeueMethod, cellReuseIdentifier: String? = nil, cellConfigurator: @escaping (Cell, Int, Section) -> Void, headerConfiguration: CollectionUIViewProvider? = nil, footerConfiguration: CollectionUIViewProvider? = nil, backgroundViewClass: UICollectionReusableView.Type? = nil) {
self.section = section
self.prototypeProvider = prototype
self.dequeueMethod = cellDequeueMethod
self.configureCell = { [weak section] c, index in
guard let cell = c as? Cell else {
assertionFailure("Got an unknown cell. Expecting cell of type \(Cell.self), got \(c)")
return
}
guard let section = section else {
assertionFailure("Asked to configure cell after section has been deallocated")
return
}
cellConfigurator(cell, index, section)
}
self.headerConfiguration = headerConfiguration
self.footerConfiguration = footerConfiguration
self.backgroundViewClass = backgroundViewClass
}

public func configure(cell: UICollectionViewCell, at index: Int) {
configureCell(cell, index)
}

}
Loading