基于此article,我为UICollectionView
创建了可重用的数据源,如下所示:-
final class CollectionViewDataSource<Model>: NSObject, UICollectionViewDataSource {
typealias CellConfigurator = (Model, UICollectionViewCell) -> Void
var models: [Model] = []
private let reuseIdentifier: String
private let cellConfigurator: CellConfigurator
init(reuseIdentifier: String, cellConfigurator: @escaping CellConfigurator) {
self.reuseIdentifier = reuseIdentifier
self.cellConfigurator = cellConfigurator
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return models.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let model = models[indexPath.item]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
cellConfigurator(model, cell)
return cell
}
}
然后我扩展了此类,因此我可以根据模型的类型提供“特定于单元格”的设置
extension CollectionViewDataSource where Model == HomeFeedItem {
static func make(reuseIdentifier: String = "FEED_ARTICLE_CELL") -> CollectionViewDataSource {
return CollectionViewDataSource(reuseIdentifier: reuseIdentifier, cellConfigurator: { item, cell in
(cell as? FeedArticleCell)?.render(with: item)
})
}
}
extension CollectionViewDataSource where Model == HomeFeedAlertItem {
static func make(reuseIdentifier: String = "FEED_ALERT_CELL") -> CollectionViewDataSource {
return CollectionViewDataSource(reuseIdentifier: reuseIdentifier, cellConfigurator: { item, cell in
(cell as? FeedAlertCell)?.render(with: item)
})
}
}
这是完美的工作,但是,这些单元格中的每个单元格都有不同的设计,但实际上却接受非常相似的属性(其他单元格也是如此)-因此,我正在考虑创建一个简单的FeedItemModel
并在呈现我的Feed之前先映射这些属性。这样可以确保在渲染提要项的任何地方,我始终都处理相同的属性。
考虑到这一点,我试图创建类似:-
extension CollectionViewDataSource where Model == FeedItemModel {
static func make(reuseIdentifier: String = "FEED_ARTICLE_CELL") -> CollectionViewDataSource {
return CollectionViewDataSource(reuseIdentifier: reuseIdentifier, cellConfigurator: { item, cell in
switch item.type {
case .news: (cell as? FeedArticleCell)?.render(with: item)
case .alert: (cell as? FeedAlertCell)?.render(with: item)
}
})
}
}
但是,由于reuseIdentifier
为item.type
,.alert
字段不再正确,因此该值下降。
如何重构此模式以允许我在同一模型中使用不同的单元格类型?还是我应该放弃这种方法而对每种单元格类型使用不同的模型,而不管输入属性是否相同?
答案 0 :(得分:1)
您可以将关联标识符添加到您的FeedItemModelType
var reuseIdentifier: String {
switch self {
case .news: return "NEW_CELL"
case .alert: return "ALERT_CELL"
}
}
您的工厂方法将如下所示
extension CollectionViewDataSource where Model == FeedItemModel {
static func make() -> CollectionViewDataSource {
return CollectionViewDataSource(reuseIdentifier: item.type.reuseIdentifier, cellConfigurator: { item, cell in
switch item.type {
case .news: (cell as? FeedArticleCell)?.render(with: item)
case .alert: (cell as? FeedAlertCell)?.render(with: item)
}
})
}
}
答案 1 :(得分:1)
您可以创建一个协议,例如
protocol FeedRenderable {
var reuseIdentifier: String { get }
}
然后确保Model
类型符合FeedRenderable
。
然后您可以将CollectionViewDataSource
重构为
final class CollectionViewDataSource<Model>: NSObject, UICollectionViewDataSource where Model: FeedRenderable {
typealias CellConfigurator = (Model, UICollectionViewCell) -> Void
var models: [Model] = []
private let cellConfigurator: CellConfigurator
init(_ cellConfigurator: @escaping CellConfigurator) {
self.cellConfigurator = cellConfigurator
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return models.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let model = models[indexPath.item]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: model.reuseIdentifier, for: indexPath)
cellConfigurator(model, cell)
return cell
}
}
注意以下更改
final class CollectionViewDataSource<Model>: NSObject, UICollectionViewDataSource where Model: FeedRenderable {
....
init(_ cellConfigurator: @escaping CellConfigurator)
....
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: model.reuseIdentifier, for: indexPath)
....
然后您可以确保根据reuseIdentifier
属性将传入的任何模型设置为其item.type
extension GenericFeedItem: FeedRenderable {
var reuseIdentifier: String {
switch type {
case .news: return "FEED_ARTICLE_CELL"
case .alert: return "FEED_ALERT_CELL"
}
}
}
您的扩展名将变为
extension CollectionViewDataSource where Model == GenericFeedItem {
static func make() -> CollectionViewDataSource {
return CollectionViewDataSource() { item, cell in
(cell as? FeedArticleCell)?.render(with: item)
(cell as? FeedAlertCell)?.render(with: item)
}
}
}
答案 2 :(得分:0)
如何为单元格添加第二个通用类型
final class CollectionViewDataSource<Model, CellType : UICollectionViewCell>: NSObject, UICollectionViewDataSource {
typealias CellConfigurator = (Model, CellType) -> Void
var models: [Model] = []
private let reuseIdentifier: String
private let cellConfigurator: CellConfigurator
init(reuseIdentifier: String, cellConfigurator: @escaping CellConfigurator) {
self.reuseIdentifier = reuseIdentifier
self.cellConfigurator = cellConfigurator
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return models.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let model = models[indexPath.item]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! CellType
cellConfigurator(model, cell)
return cell
}
}