Last active
September 20, 2016 11:05
-
-
Save rnapier/44bf23300959cca9c43f to your computer and use it in GitHub Desktop.
Based on discussion at https://gist.github.com/jessesquires/4b6e76bb580600a85c40
This file contains hidden or 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
// We're going to try to build a collectionview layout engine that can serve up cells and supplementary views | |
// in a generic way. I haven't built this all the way to the collection view, and I don't konw what the real | |
// brief is, so there may be lots of gotchas here that don't work as desired, but it scopes out several ways | |
// to get a handle on the problem and how I'd at least probably start. | |
// Ultimately, this requires converting (Model, IndexPath) -> Cell and (Model, IndexPath) -> SupView. I'm | |
// assuming that's the only feature of these factories. |
This file contains hidden or 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
// First we'll try it with straight protocols and generics. This is fairly straightforward, but has a problem. | |
// There's no way to force CellFactoryType and SupplementaryViewFactoryType to have the same Model. That | |
// might be ok, but I suspect in real usage it would tend to blow up. But let's start with this anyway. | |
// Note also that this invents a EmptySupplementaryViewFactory so that we have some type to feed into the | |
// layout engine when we have no type. There has to be *some* type, so we invent one. | |
import UIKit | |
protocol CellFactoryType { | |
typealias Cell: UICollectionViewCell | |
typealias Model | |
func cellForModel(model: Model, indexPath: NSIndexPath) -> Cell | |
} | |
protocol SupplementaryViewFactoryType { | |
typealias View: UICollectionReusableView | |
typealias Model | |
func suplementaryViewForModel(model: Model, indexPath: NSIndexPath) -> View? | |
} | |
struct EmptySupplementaryViewFactory<Model>: SupplementaryViewFactoryType { | |
typealias View = UICollectionReusableView | |
func suplementaryViewForModel(model: Model, indexPath: NSIndexPath) -> View? { return nil } | |
} | |
protocol LayoutEngineType { | |
typealias CellFactory: CellFactoryType | |
typealias SupplementaryViewFactory: SupplementaryViewFactoryType | |
var cellFactory: CellFactory { get } | |
var supplementaryViewFactory: SupplementaryViewFactory { get } | |
} | |
struct LayoutEngine<CellFactory: CellFactoryType, SupplementaryViewFactory: SupplementaryViewFactoryType | |
where CellFactory.Model == SupplementaryViewFactory.Model> : LayoutEngineType { | |
let cellFactory: CellFactory | |
let supplementaryViewFactory: SupplementaryViewFactory | |
init(cellFactory: CellFactory, supplementaryViewFactory: SupplementaryViewFactory) { | |
self.cellFactory = cellFactory | |
self.supplementaryViewFactory = supplementaryViewFactory | |
} | |
} | |
struct SimpleLayoutEngine<CellFactory: CellFactoryType> : LayoutEngineType { | |
typealias SupplementaryViewFactory = EmptySupplementaryViewFactory<CellFactory.Model> | |
let cellFactory: CellFactory | |
var supplementaryViewFactory: SupplementaryViewFactory { return SupplementaryViewFactory() } | |
init(cellFactory: CellFactory) { | |
self.cellFactory = cellFactory | |
} | |
} |
This file contains hidden or 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
// That's ok, but we can do a little better by adding type erasers. With these, we can actually force all the | |
// models to match. On the other hand, this code is getting really unweildy. | |
import UIKit | |
protocol CellFactoryType { | |
typealias Cell: UICollectionViewCell | |
typealias Model | |
func cellForModel(model: Model, indexPath: NSIndexPath) -> Cell | |
} | |
struct AnyCellFactory<Model, Cell: UICollectionViewCell>: CellFactoryType { | |
init<C: CellFactoryType where C.Model == Model, C.Cell == Cell>(_ factory: C) { | |
_cellForModel = { (model: Model, indexPath: NSIndexPath) -> Cell in | |
return factory.cellForModel(model, indexPath: indexPath) | |
} | |
} | |
let _cellForModel: (model: Model, indexPath: NSIndexPath) -> Cell | |
func cellForModel(model: Model, indexPath: NSIndexPath) -> Cell { | |
return _cellForModel(model: model, indexPath: indexPath) | |
} | |
} | |
protocol SupplementaryViewFactoryType { | |
typealias View: UICollectionReusableView | |
typealias Model | |
func suplementaryViewForModel(model: Model, indexPath: NSIndexPath) -> View? | |
} | |
struct AnySupplementaryViewFactory<Model, View: UICollectionReusableView>: SupplementaryViewFactoryType { | |
init<S: SupplementaryViewFactoryType where S.Model == Model, S.View == View>(_ factory: S) { | |
_suplementaryViewForModel = { (model: Model, indexPath: NSIndexPath) -> View? in | |
return factory.suplementaryViewForModel(model, indexPath: indexPath) | |
} | |
} | |
let _suplementaryViewForModel: (model: Model, indexPath: NSIndexPath) -> View? | |
func suplementaryViewForModel(model: Model, indexPath: NSIndexPath) -> View? { | |
return _suplementaryViewForModel(model: model, indexPath: indexPath) | |
} | |
} | |
struct EmptySupplementaryViewFactory<Model>: SupplementaryViewFactoryType { | |
typealias View = UICollectionReusableView | |
func suplementaryViewForModel(model: Model, indexPath: NSIndexPath) -> View? { return nil } | |
} | |
protocol LayoutEngineType { | |
typealias CellFactory: CellFactoryType | |
typealias SupplementaryViewFactory: SupplementaryViewFactoryType | |
var cellFactory: CellFactory { get } | |
var supplementaryViewFactory: SupplementaryViewFactory { get } | |
} | |
struct LayoutEngine<Model, Cell: UICollectionViewCell, View: UICollectionReusableView> : LayoutEngineType { | |
let cellFactory: AnyCellFactory<Model, Cell> | |
let supplementaryViewFactory: AnySupplementaryViewFactory<Model, View> | |
init<CellFactory: CellFactoryType, SupplementaryViewFactory: SupplementaryViewFactoryType | |
where SupplementaryViewFactory.Model == Model, | |
CellFactory.Model == Model, | |
CellFactory.Cell == Cell, | |
SupplementaryViewFactory.View == View> | |
(cellFactory: CellFactory, supplementaryViewFactory: SupplementaryViewFactory) { | |
self.cellFactory = AnyCellFactory(cellFactory) | |
self.supplementaryViewFactory = AnySupplementaryViewFactory(supplementaryViewFactory) | |
} | |
} | |
struct SimpleLayoutEngine<Model, Cell: UICollectionViewCell> : LayoutEngineType { | |
typealias SupplementaryViewFactory = EmptySupplementaryViewFactory<Model> | |
let cellFactory: AnyCellFactory<Model, Cell> | |
var supplementaryViewFactory: SupplementaryViewFactory { return SupplementaryViewFactory() } | |
init<CellFactory: CellFactoryType | |
where CellFactory.Model == Model, CellFactory.Cell == Cell>(cellFactory: CellFactory) { | |
self.cellFactory = AnyCellFactory(cellFactory) | |
} | |
} |
This file contains hidden or 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
// But what if we ditched this whole "factory" thing. Get rid of the extra classes entirely. They're just big | |
// containers to hold a single function. What if we just used the function itself? Well, the code gets a *lot* | |
// simpler. And this is what I'd probably lean towards if I were really building this. | |
import UIKit | |
protocol LayoutEngineType { | |
typealias Model | |
typealias Cell: UICollectionViewCell | |
typealias SupplementaryView: UICollectionReusableView | |
var cellForModel: (model: Model, indexPath: NSIndexPath) -> Cell { get } | |
var suplementaryViewForModel: (model: Model, indexPath: NSIndexPath) -> SupplementaryView? { get } | |
// Or suplementaryViewForModel could be an optional func rather than a func that returned an optional. | |
// That would let you test whether this function was implemented (which is probably necessary in reality). | |
// Almost like... @optional. Funny that, huh? | |
} | |
struct LayoutEngine<Model, Cell: UICollectionViewCell, SupplementaryView: UICollectionReusableView> : LayoutEngineType { | |
let cellForModel: (model: Model, indexPath: NSIndexPath) -> Cell | |
let suplementaryViewForModel: (model: Model, indexPath: NSIndexPath) -> SupplementaryView? | |
init(cellForModel: (model: Model, indexPath: NSIndexPath) -> Cell, | |
suplementaryViewForModel: (model: Model, indexPath: NSIndexPath) -> SupplementaryView?) { | |
self.cellForModel = cellForModel | |
self.suplementaryViewForModel = suplementaryViewForModel | |
} | |
} | |
struct SimpleLayoutEngine<Model, Cell: UICollectionViewCell> : LayoutEngineType { | |
let cellForModel: (model: Model, indexPath: NSIndexPath) -> Cell | |
let suplementaryViewForModel: (model: Model, indexPath: NSIndexPath) -> UICollectionReusableView? = { _,_ in return nil } | |
init(cellForModel: (model: Model, indexPath: NSIndexPath) -> Cell) { | |
self.cellForModel = cellForModel | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@rnapier this is awesome!
Great idea about ditching types and simply using closures.
I'll have to see if I can work out a similar approach to use in JSQDataSourcesKit.