-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #209 from Shopify/jk_ftd2
Provide first-class support for UICollectionView
- Loading branch information
Showing
30 changed files
with
2,440 additions
and
325 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,91 @@ | ||
// | ||
// AnyHashableConfig.swift | ||
// FunctionalTableData | ||
// | ||
// Created by Jason Kemp on 2021-10-27. | ||
// | ||
|
||
import UIKit | ||
|
||
public struct AnyHashableConfig: Hashable, HashableCellConfigType { | ||
public static func ==(lhs: AnyHashableConfig, rhs: AnyHashableConfig) -> Bool { | ||
return lhs.hashable == rhs.hashable | ||
} | ||
|
||
private var base: CellConfigType | ||
public let hashable: AnyHashable | ||
|
||
public func hash(into hasher: inout Hasher) { | ||
hasher.combine(hashable) | ||
} | ||
|
||
public init(_ base: CellConfigType, sectionKey: String) { | ||
self.base = base | ||
if let hashableConfig = base as? HashableCellConfigType { | ||
self.hashable = hashableConfig.hashable | ||
} else { | ||
self.hashable = AnyHashable(ItemPath(sectionKey: sectionKey, itemKey: base.key)) | ||
} | ||
} | ||
|
||
public init(_ base: HashableCellConfigType) { | ||
self.base = base | ||
self.hashable = base.hashable | ||
} | ||
|
||
public var key: String { | ||
return base.key | ||
} | ||
|
||
public var style: CellStyle? { | ||
get { return base.style } | ||
set { base.style = newValue } | ||
} | ||
|
||
public var actions: CellActions { | ||
get { return base.actions } | ||
set { base.actions = newValue } | ||
} | ||
|
||
public var accessibility: Accessibility { | ||
get { return base.accessibility } | ||
set { base.accessibility = newValue } | ||
} | ||
|
||
public func dequeueCell(from tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell { | ||
return base.dequeueCell(from: tableView, at: indexPath) | ||
} | ||
|
||
public func dequeueCell(from collectionView: UICollectionView, at indexPath: IndexPath) -> UICollectionViewCell { | ||
return base.dequeueCell(from: collectionView, at: indexPath) | ||
} | ||
|
||
public func update(cell: UITableViewCell, in tableView: UITableView) { | ||
base.update(cell: cell, in: tableView) | ||
} | ||
|
||
public func update(cell: UICollectionViewCell, in collectionView: UICollectionView) { | ||
base.update(cell: cell, in: collectionView) | ||
} | ||
|
||
public func isEqual(_ other: CellConfigType) -> Bool { | ||
guard let other = other as? AnyHashableConfig else { return false } | ||
return base.isEqual(other.base) | ||
} | ||
|
||
public func isSameKind(as other: CellConfigType) -> Bool { | ||
return base.isSameKind(as: other) | ||
} | ||
|
||
public func debugInfo() -> [String : Any] { | ||
return base.debugInfo() | ||
} | ||
|
||
public func register(with tableView: UITableView) { | ||
base.register(with: tableView) | ||
} | ||
|
||
public func register(with collectionView: UICollectionView) { | ||
base.register(with: collectionView) | ||
} | ||
} |
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
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
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,76 @@ | ||
// | ||
// CollectionSection.swift | ||
// FunctionalTableData | ||
// | ||
// Created by Jason Kemp on 2021-10-05. | ||
// Copyright © 2021 Shopify. All rights reserved. | ||
|
||
import UIKit | ||
|
||
public typealias CellPreparer = (UICollectionViewCell, UICollectionView, IndexPath) -> Void | ||
|
||
public protocol CollectionSection { | ||
var key: String { get } | ||
|
||
var items: [CellConfigType] { get set } | ||
|
||
var supplementaries: [CollectionSupplementaryItemConfig] { get set } | ||
|
||
/// Callback executed when an item is manually moved by the user. It specifies the before and after index position. | ||
var didMoveRow: ((_ from: Int, _ to: Int) -> Void)? { get } | ||
|
||
func prepareCell(_ cell: UICollectionViewCell, in collectionView: UICollectionView, for indexPath: IndexPath) | ||
} | ||
|
||
public protocol HashableCellConfigType: CellConfigType { | ||
var hashable: AnyHashable { get } | ||
} | ||
|
||
public extension CollectionSection { | ||
func supplementaryConfig(ofKind kind: ReusableKind) -> CollectionSupplementaryItemConfig? { | ||
return supplementaries.first(where: { $0.kind == kind }) | ||
} | ||
|
||
var header: CollectionSupplementaryItemConfig? { | ||
return supplementaries.first(where: { $0.kind == .header }) | ||
} | ||
|
||
var footer: CollectionSupplementaryItemConfig? { | ||
return supplementaries.first(where: { $0.kind == .footer }) | ||
} | ||
} | ||
|
||
public struct SimpleCollectionSection: CollectionSection, Hashable { | ||
public static func ==(lhs: SimpleCollectionSection, rhs: SimpleCollectionSection) -> Bool { | ||
return lhs.key == rhs.key | ||
} | ||
|
||
public let key: String | ||
public var items: [CellConfigType] | ||
public var supplementaries: [CollectionSupplementaryItemConfig] | ||
|
||
public init(key: String, | ||
items: [CellConfigType], | ||
supplementaries: [CollectionSupplementaryItemConfig] = [], | ||
didMoveRow: ((Int, Int) -> Void)? = nil, | ||
cellPreparer: CellPreparer? = nil) { | ||
self.key = key | ||
self.items = items | ||
self.supplementaries = supplementaries | ||
self.didMoveRow = didMoveRow | ||
self.cellPreparer = cellPreparer | ||
} | ||
|
||
public func hash(into hasher: inout Hasher) { | ||
hasher.combine(key) | ||
} | ||
|
||
/// Callback executed when an item is manually moved by the user. It specifies the before and after index position. | ||
public let didMoveRow: ((_ from: Int, _ to: Int) -> Void)? | ||
|
||
public var cellPreparer: CellPreparer? | ||
|
||
public func prepareCell(_ cell: UICollectionViewCell, in collectionView: UICollectionView, for indexPath: IndexPath) { | ||
cellPreparer?(cell, collectionView, indexPath) | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
Sources/FunctionalTableData/CollectionView/AnyCollectionSection.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,39 @@ | ||
// | ||
// AnyCollectionSection.swift | ||
// FunctionalTableData | ||
// | ||
// Created by Jason Kemp on 2021-10-28. | ||
// | ||
|
||
import UIKit | ||
|
||
struct AnyCollectionSection: Hashable { | ||
public static func ==(lhs: AnyCollectionSection, rhs: AnyCollectionSection) -> Bool { | ||
return lhs.key == rhs.key | ||
} | ||
|
||
private var impl: CollectionSection | ||
|
||
public var key: String { impl.key } | ||
public var items: [HashableCellConfigType] | ||
public var supplementaries: [CollectionSupplementaryItemConfig] { | ||
get { impl.supplementaries } | ||
set { impl.supplementaries = newValue } | ||
} | ||
|
||
public init(_ section: CollectionSection) { | ||
items = section.items.map { AnyHashableConfig($0, sectionKey: section.key) } | ||
impl = section | ||
} | ||
|
||
public func hash(into hasher: inout Hasher) { | ||
hasher.combine(key) | ||
} | ||
|
||
/// Callback executed when a item is manually moved by the user. It specifies the before and after index position. | ||
public var didMoveRow: ((_ from: Int, _ to: Int) -> Void)? { impl.didMoveRow } | ||
|
||
public func prepareCell(_ cell: UICollectionViewCell, in collectionView: UICollectionView, for indexPath: IndexPath) { | ||
impl.prepareCell(cell, in: collectionView, for: indexPath) | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
Sources/FunctionalTableData/CollectionView/CellConfig.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,70 @@ | ||
// | ||
// CellConfig.swift | ||
// | ||
// | ||
// Created by Jason Kemp on 2021-10-19. | ||
// | ||
|
||
import UIKit | ||
|
||
public struct CellConfig<View, State>: HashableCellConfigType where View: UIView & ConfigurableView, State: Hashable, View.State == State { | ||
public typealias CollectionCellType = ConfigurableCollectionCell<View, State> | ||
|
||
public let key: String | ||
public var hashable: AnyHashable { return AnyHashable(state) } | ||
public var style: CellStyle? | ||
public var actions: CellActions | ||
public var accessibility: Accessibility | ||
public let state: State | ||
|
||
public init(key: String, | ||
style: CellStyle? = CellStyle(backgroundColor: nil), | ||
actions: CellActions = CellActions(), | ||
accessibility: Accessibility = Accessibility(), | ||
state: State) { | ||
self.key = key | ||
self.style = style | ||
self.actions = actions | ||
self.accessibility = accessibility | ||
self.state = state | ||
} | ||
|
||
public func isEqual(_ other: CellConfigType) -> Bool { | ||
guard let other = other as? CellConfig<View, State> else { | ||
return false | ||
} | ||
return state == other.state && accessibility == other.accessibility | ||
} | ||
|
||
public func debugInfo() -> [String : Any] { | ||
return ["key": key, "type": String(describing: type(of: self))] | ||
} | ||
|
||
// MARK: - TableItemConfigType | ||
public func register(with tableView: UITableView) { | ||
// intentionally blank, intended use for CellConfig is for UICollectionView | ||
} | ||
|
||
public func dequeueCell(from tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell { | ||
return UITableViewCell() | ||
} | ||
|
||
public func update(cell: UITableViewCell, in tableView: UITableView) { | ||
// intentionally blank, intended use for CellConfig is for UICollectionView | ||
} | ||
|
||
// MARK: - CollectionItemConfigType | ||
|
||
public func register(with collectionView: UICollectionView) { | ||
collectionView.registerReusableCell(CollectionCellType.self) | ||
} | ||
|
||
public func dequeueCell(from collectionView: UICollectionView, at indexPath: IndexPath) -> UICollectionViewCell { | ||
return collectionView.dequeueReusableCell(CollectionCellType.self, indexPath: indexPath) | ||
} | ||
|
||
public func update(cell: UICollectionViewCell, in collectionView: UICollectionView) { | ||
guard let cell = cell as? CollectionCellType else { return } | ||
cell.configure(state) | ||
} | ||
} |
Oops, something went wrong.