关于
Collor 是一个用于加速、简化并确保 UICollectionView 构建的数据驱动 MVVM 框架。
Collor 用于并改进了 Voyages-sncf.com 应用。
特性
以下是所有特性的列表
- 易于使用。
- 可读的collectionView模型。
- 设计为可重用cell。
- 协议/结构面向。
- 可扩展。
- 决不使用
IndexPath
。 - 决不注册cell。
- 轻松更新collectionView模型。
- 选择数据或部分
- 选择处理删除、插入、移动和更新
- 轻松管理自定义布局中的装饰视图。
- 简化自定义布局的构建。
- Swift 4(使用1.0.x以实现Swift 3兼容性)。
- 经过充分测试。
-
🆕 处理辅助视图(可能在下一个版本中改进)
开始使用
- 一篇文章medium article,解释了 Collor 的目的和使用方法。
- 另一篇medium article,了解 Collor 的差异功能。
- 如何使用 Collor 创建离线 CollectionView 类型的 API
示例
要运行示例项目,请先克隆仓库,然后从示例目录运行pod install
。
这里有4个示例
- 菜单:带有用户事件传播的简单collectionView示例
- 随机:完整数据diffing +自定义布局
- 天气:区域diffing +自定义布局
- 潘通:使用CollectionDatas添加和删除项目。
- 实时:复杂diffing(插入、删除、重新加载)+带有
DecorationViewHandler
的自定义布局处理。 - 字母:一个带
supplementaryView
的示例,以及使用SupplementaryViewsHandler
的自定义布局处理。
使用
UICollectionView由一个包含sectionDescriptors的collectionData对象表示,而sectionDescriptors又包含cellDescriptors。Collor中的每个条目或单元组成由3个对象
- 实现
CollectionCellAdaptable
的UICollectionViewCell
(是否为XIB以及Swift文件) - 实现
CollectionCellDescribable
的cellDescriptor - 实现
CollectionAdapter
的适配器(视图模型)
CellDescriptor
它描述了单元格,是单元格和viewModel之间的连接。逻辑上,一种类型的单元格只需要一个cellDescriptor。它拥有单元格标识符、单元格className和处理单元格大小的操作。collectionData使用这些属性处理单元格的注册和撤回。
final class WeatherDayDescriptor: CollectionCellDescribable {
let identifier: String = "WeatherDayCollectionViewCell"
let className: String = "WeatherDayCollectionViewCell"
var selectable:Bool = false
let adapter: WeatherDayAdapter
init(adapter: WeatherDayAdapter) {
self.adapter = adapter
}
func size(_ collectionView: UICollectionView, sectionDescriptor: CollectionSectionDescribable) -> CGSize {
let sectionInset = sectionDescriptor.sectionInset(collectionView)
let width:CGFloat = collectionView.bounds.width - sectionInset.left - sectionInset.right
return CGSize(width:width, height:60)
}
public func getAdapter() -> CollectionAdapter {
return adapter
}
}
适配器
适配器是一个viewModel对象。它将您的模型转换成人可读的数据,用于单元格。
struct WeatherDayAdapter: CollectionAdapter {
let date: NSAttributedString
static let dateFormatter: DateFormatter = {
let df = DateFormatter()
df.dateFormat = "EEEE d MMMM"
return df
}()
init(day:WeatherDay) {
let dateString = WeatherDayAdapter.dateFormatter.string(from: day.date)
date = NSAttributedString(string: dateString, attributes: [
NSFontAttributeName: UIFont.boldSystemFont(ofSize: 18),
NSForegroundColorAttributeName: UIColor.black
])
}
}
当单元格被出列时,collectionData会更新此对象用于单元格。
final class WeatherDayCollectionViewCell: UICollectionViewCell, CollectionCellAdaptable {
@IBOutlet weak var label: UILabel!
func update(with adapter: CollectionAdapter) {
guard let adapter = adapter as? WeatherDayAdapter else {
fatalError("WeatherDayAdapter required")
}
label.attributedText = adapter.date
}
}
ViewController
创建dataSource和delegate
lazy var collectionViewDelegate: CollectionDelegate = CollectionDelegate(delegate: self)
lazy var collectionViewDatasource: CollectionDataSource = CollectionDataSource(delegate: self)
创建collectionData
let collectionData = MyCollectionData()
将collectionView与数据、dataSource和delegate绑定
bind(collectionView: collectionView, with: collectionData, and: collectionViewDelegate, and: collectionViewDatasource)
在collectionData中创建一个section和一个cell
final class MyCollectionData : CollectionData {
override func reloadData() {
super.reloadData()
let section = MySectionDescriptor().reloadSection { (cells) in
let cell = MyColorDescriptor(adapter: MyColorAdapter(model: "someThing"))
cells.append(cell)
})
}
sections.append(section)
}
}
MySectionDescriptor
final class MySectionDescriptor : CollectionSectionDescribable {
func sectionInset(_ collectionView: UICollectionView) -> UIEdgeInsets {
return UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10)
}
}
你可以得到表示UICollectionView的可读数据,无需代码重复,重用cell和良好的代码分离。
补充视图
要添加补充视图,而不是使用CollectionSectionDescribable.reloadSection(:)
,调用CollectionSectionDescribable.reload(:)
。作为参数传递的builder
公开了添加补充视图
的新方法。
let section = MySectionDescriptor().reload { builder in
let letterAdapter = LetterAdapter(letter: "A")
let letterDescriptor = LetterCollectionReusableViewDescriptor(adapter: letterAdapter)
builder.add(supplementaryView: letterDescriptor, kind: "letter")
}
sections.append(section)
CollectionSupplementaryViewDescribable
的行为类似于CollectionCellDescribable
。它需要一个适配器来填充视图中的数据。但是,为了正确显示补充视图,需要自定义布局来为在CollectionData
中创建的补充视图设置属性。与装饰视图一样,Collor提供了一个SupplementaryViewsHandler
对象来管理补充视图。它处理添加、缓存和更新。
class Layout : UICollectionViewFlowLayout {
//...
override func prepare() {
super.prepare()
supplementaryViewsHandler.prepare()
for (sectionIndex, sectionDescriptor) in datas.sections.enumerated() {
let firstCellIndexPath = IndexPath(item: 0, section: sectionIndex)
let firstCellAttributes = layoutAttributesForItem(at: firstCellIndexPath)!
sectionDescriptor.supplementaryViews.forEach { (kind, views) in
views.enumerated().forEach { (index, viewDescriptor) in
let indexPath = IndexPath(item: index, section: sectionIndex)
let a = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: kind, with: indexPath)
a.frame = viewDescriptor.frame(collectionView, sectionDescriptor: sectionDescriptor)
a.frame.origin.y += firstCellAttributes.frame.origin.y
supplementaryViewsHandler.add(attributes: a)
}
}
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributes = super.layoutAttributesForElements(in: rect)
let supplementaryAttributes = supplementaryViewsHandler.attributes(in: rect)
if let attributes = attributes {
return attributes + supplementaryAttributes
}
return attributes
}
override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
return supplementaryViewsHandler.attributes(for: elementKind, at: indexPath)
}
override func prepare(forCollectionViewUpdates updateItems: [UICollectionViewUpdateItem]) {
super.prepare(forCollectionViewUpdates: updateItems)
supplementaryViewsHandler.prepare(forCollectionViewUpdates: updateItems)
}
override func indexPathsToInsertForSupplementaryView(ofKind elementKind: String) -> [IndexPath] {
return supplementaryViewsHandler.inserted(for: elementKind)
}
override func indexPathsToDeleteForSupplementaryView(ofKind elementKind: String) -> [IndexPath] {
return supplementaryViewsHandler.deleted(for: elementKind)
}
}
对比和更新
Collor 提供了一些功能,可以轻松更新您的 collectionData。
正在更新
只需使用CollectionData.update(_:)
方法来添加或删除单元格或部分。这意味着不再需要与IndexPath
进行繁琐的互动
let newCellDescriptor = NewCellDescriptor(...)
let result = collectionData.update { updater in
updater.append(cells: [newCellDescriptor], after: anotherCellDescriptor)
}
collectionView.performUpdates(with: result)
以下是所有可用的更新方法列表
- append(cells:after:)
- append(cells:before:)
- append(cells:in:)
- remove(cells:)
- reload(cells:)
- append(sections:after:)
- append(sections:before:)
- append(sections:)
- remove(sections:)
- reload(sections:)
差异比较
Collor正在使用自定的算法来获取你的collectionData两次更新之间的"差异"。
- 比较一些部分
sectionDescriptor.isExpanded = !sectionDescriptor.isExpanded
let result = collectionData.update{ updater in
updater.diff(sections: [sectionDescriptor])
}
collectionView.performUpdates(with: result)
- 比较整个数据
model.someUpdates()
let result = collectionData.update { updater in
collectionData.reload(model: model)
updater.diff()
}
collectionView.performUpdates(with: result)
- 轻松管理装饰视图
使用DecorationViewHandler
,你不再需要实现管理装饰视图的代码
// register decoration view or class:
decorationViewHandler.register(viewClass: SimpleDecorationView.self, for: sectionBackgroundKind)
// caching
decorationViewHandler.add(attributes: backgroundAttributes)
// compute elements in rect
decorationViewHandler.attributes(in:rect)
// retrieving
decorationViewHandler.attributes(for: elementKind, at: atIndexPath)
// update handling
decorationViewHandler.prepare(forCollectionViewUpdates: updateItems)
return decorationViewHandler.inserted(for: elementKind)
return decorationViewHandler.deleted(for: elementKind)
获取更多信息,请查看这篇文章medium article。
XCTemplates
Collor包装了3个XCTemplates,帮助您创建 ViewController、SectionDescriptor 和 CellDescriptor。
要安装它们,只需进入 xctemplates 目录,并在终端中运行以下命令:
sh install.sh
需求
- iOS 10.0+
- Swift 4.2+ (获取 swift3.x 版本的 1.0.3 释放,swift4.0 版本的 1.1.23)
- Xcode 10.1+
安装
CocoaPods
Collor 可以通过 CocoaPods 获取。要安装它,只需将以下行添加到您的 Podfile 中:
pod "Collor"
Collor 尚不支持 Carthage。正在进行中...
文档
工作正在进行中... 已完成文档 1%
致谢
Collor 由 oui.sncf 拥有并维护。
Collor 最初由 Gwenn Guihal 创建。
许可
Color可在BSD许可下使用。有关更多信息,请参阅LICENSE文件。