Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Init CollectionView * Add Collection Example * Add CollectionViewBox to project * Format code * Refactor Collection View * Fix Collection View example style * Add rule to hound.yml * Format code * Improve error message
- Loading branch information
Showing
13 changed files
with
371 additions
and
6 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 |
---|---|---|
@@ -1,2 +1,4 @@ | ||
fail_on_violations: true | ||
|
||
swiftlint: | ||
config_file: .swiftlint.yml |
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,66 @@ | ||
// | ||
// CollectionExample.swift | ||
// TokamakDemo | ||
// | ||
// Created by Matvii Hodovaniuk on 3/7/19. | ||
// Copyright © 2019 Tokamak. All rights reserved. | ||
// | ||
|
||
import Tokamak | ||
|
||
enum ElementaryParticles: String, CaseIterable { | ||
case up | ||
case down | ||
case charm | ||
case strange | ||
case top | ||
case bottom | ||
case gluon | ||
case photon | ||
case higs | ||
case electron | ||
case muon | ||
case tau | ||
case electronNeutrino = "Electron Neutrino" | ||
case muonNeutrino = "Muon Neutrino" | ||
case tauNeutrino = "Tau Neutrino" | ||
case zBoson = "Z Boson" | ||
case wBoson = "W Boson" | ||
} | ||
|
||
extension ElementaryParticles: CustomStringConvertible { | ||
var description: String { return rawValue.localizedCapitalized } | ||
} | ||
|
||
private struct Cells: SimpleCellProvider { | ||
static func cell( | ||
props: Null, | ||
item: ElementaryParticles, | ||
path: CellPath | ||
) -> AnyNode { | ||
return Label.node(.init(Style( | ||
[CenterY.equal(to: .parent), | ||
Height.equal(to: 44), | ||
Leading.equal(to: .parent), | ||
Trailing.equal(to: .parent)] | ||
)), "\(item.description)") | ||
} | ||
|
||
typealias Props = Null | ||
|
||
typealias Model = [[ElementaryParticles]] | ||
} | ||
|
||
struct CollectionExample: PureLeafComponent { | ||
typealias Props = Null | ||
|
||
static func render(props: Props) -> AnyNode { | ||
return CollectionView<Cells>.node(.init( | ||
Style( | ||
Edges.equal(to: .parent, inset: 20), | ||
backgroundColor: .white | ||
), | ||
model: [ElementaryParticles.allCases] | ||
)) | ||
} | ||
} |
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,65 @@ | ||
// | ||
// CollectionView.swift | ||
// Tokamak | ||
// | ||
// Created by Matvii Hodovaniuk on 3/6/19. | ||
// | ||
|
||
public struct CollectionView<T: CellProvider>: HostComponent { | ||
public struct Props: Equatable, StyleProps { | ||
public let cellProps: T.Props | ||
public let model: T.Model | ||
public let onSelect: Handler<CellPath>? | ||
public let style: Style? | ||
|
||
public init( | ||
_ style: Style? = nil, | ||
cellProps: T.Props, | ||
onSelect: Handler<CellPath>? = nil, | ||
singleSection: T.Model.Element | ||
) { | ||
self.cellProps = cellProps | ||
model = T.Model.single(section: singleSection) | ||
self.onSelect = onSelect | ||
self.style = style | ||
} | ||
|
||
public init( | ||
_ style: Style? = nil, | ||
cellProps: T.Props, | ||
model: T.Model, | ||
onSelect: Handler<CellPath>? = nil | ||
) { | ||
self.cellProps = cellProps | ||
self.model = model | ||
self.onSelect = onSelect | ||
self.style = style | ||
} | ||
} | ||
|
||
public typealias Children = Null | ||
} | ||
|
||
extension CollectionView.Props where T.Props == Null { | ||
public init( | ||
_ style: Style? = nil, | ||
model: T.Model, | ||
onSelect: Handler<CellPath>? = nil | ||
) { | ||
cellProps = Null() | ||
self.model = model | ||
self.onSelect = onSelect | ||
self.style = style | ||
} | ||
|
||
public init( | ||
_ style: Style? = nil, | ||
onSelect: Handler<CellPath>? = nil, | ||
singleSection: T.Model.Element | ||
) { | ||
cellProps = Null() | ||
model = T.Model.single(section: singleSection) | ||
self.onSelect = onSelect | ||
self.style = style | ||
} | ||
} |
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,148 @@ | ||
// | ||
// CollectionViewBox.swift | ||
// TokamakUIKit | ||
// | ||
// Created by Matvii Hodovaniuk on 3/7/19. | ||
// | ||
|
||
import Tokamak | ||
import UIKit | ||
|
||
final class TokamakCollectionCell: UICollectionViewCell { | ||
// FIXME: `component` has a strong reference to `box` through its own | ||
// property `target`, should that be `weak` to break a potential reference | ||
// cycle? | ||
fileprivate var component: UIKitRenderer.Mounted? | ||
} | ||
|
||
private final class DataSource<T: CellProvider>: NSObject, | ||
UICollectionViewDataSource { | ||
weak var viewController: UIViewController? | ||
weak var renderer: UIKitRenderer? | ||
var props: CollectionView<T>.Props | ||
|
||
init( | ||
_ props: CollectionView<T>.Props, | ||
_ viewController: UIViewController, | ||
_ renderer: UIKitRenderer? | ||
) { | ||
self.props = props | ||
self.viewController = viewController | ||
self.renderer = renderer | ||
} | ||
|
||
func numberOfSections(in collectionView: UICollectionView) -> Int { | ||
return props.model.count | ||
} | ||
|
||
func collectionView( | ||
_ collectionView: UICollectionView, | ||
numberOfItemsInSection section: Int | ||
) -> Int { | ||
return props.model[section].count | ||
} | ||
|
||
func collectionView( | ||
_ collectionView: UICollectionView, | ||
cellForItemAt indexPath: IndexPath | ||
) -> UICollectionViewCell { | ||
let item = props.model[indexPath.section][indexPath.row] | ||
|
||
let (id, node) = T.cell( | ||
props: props.cellProps, | ||
item: item, | ||
path: CellPath(indexPath) | ||
) | ||
|
||
if let cell = collectionView.dequeueReusableCell( | ||
withReuseIdentifier: id.rawValue, for: indexPath | ||
) as? TokamakCollectionCell, let component = cell.component { | ||
renderer?.update(component: component, with: node) | ||
return cell | ||
} else { | ||
if let cell = collectionView.dequeueReusableCell( | ||
withReuseIdentifier: id.rawValue, for: indexPath | ||
) as? TokamakCollectionCell { | ||
if let component = cell.component { | ||
renderer?.update(component: component, with: node) | ||
} else if let viewController = viewController { | ||
cell.component = renderer?.mount( | ||
with: node, | ||
to: ViewBox(cell, viewController, node) | ||
) | ||
} | ||
return cell | ||
} else { | ||
fatalError("unknown cell type returned from dequeueReusableCell") | ||
} | ||
} | ||
} | ||
} | ||
|
||
private final class Delegate<T: CellProvider>: | ||
NSObject, | ||
UICollectionViewDelegate { | ||
var onSelect: ((CellPath) -> ())? | ||
|
||
func collectionView( | ||
_ collectionView: UICollectionView, | ||
didSelectItemAt indexPath: IndexPath | ||
) { | ||
onSelect?(CellPath(indexPath)) | ||
} | ||
|
||
init(_ props: CollectionView<T>.Props) { | ||
onSelect = props.onSelect?.value | ||
} | ||
} | ||
|
||
final class TokamakCollectionView: UICollectionView, Default { | ||
static var defaultValue: TokamakCollectionView { | ||
let layout = UICollectionViewFlowLayout() | ||
layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize | ||
return TokamakCollectionView(frame: .zero, collectionViewLayout: layout) | ||
} | ||
} | ||
|
||
final class CollectionViewBox<T: CellProvider>: ViewBox<TokamakCollectionView> { | ||
private let dataSource: DataSource<T> | ||
|
||
// this delegate stays as a constant and doesn't create a reference cycle | ||
// swiftlint:disable:next weak_delegate | ||
private let delegate: Delegate<T> | ||
|
||
var props: CollectionView<T>.Props { | ||
get { | ||
return dataSource.props | ||
} | ||
set { | ||
let oldModel = dataSource.props.model | ||
dataSource.props = newValue | ||
delegate.onSelect = newValue.onSelect?.value | ||
if oldModel != newValue.model { | ||
view.reloadData() | ||
} | ||
} | ||
} | ||
|
||
init( | ||
_ view: TokamakCollectionView, | ||
_ viewController: UIViewController, | ||
_ component: UIKitRenderer.MountedHost, | ||
_ props: CollectionView<T>.Props, | ||
_ renderer: UIKitRenderer | ||
) { | ||
dataSource = DataSource(props, viewController, renderer) | ||
delegate = Delegate(props) | ||
view.dataSource = dataSource | ||
view.delegate = delegate | ||
|
||
for id in T.Identifier.allCases { | ||
view.register( | ||
TokamakCollectionCell.self, | ||
forCellWithReuseIdentifier: id.rawValue | ||
) | ||
} | ||
super.init(view, viewController, component.node) | ||
} | ||
} |
Oops, something went wrong.