Collor 1.1.41

Collor 1.1.41

测试已测试
Lang语言 SwiftSwift
许可证 BSD-3-Clause
发布最后发布2021年8月
SPM支持 SPM

Gwenn Guihal‘OussamTlili’Mickael LaloumGhislain Deffrasnes 维护。




Collor 1.1.41


Collor logo

CI Status Coverage Status Version License Platform

关于

Collor 是一个用于加速、简化并确保 UICollectionView 构建的数据驱动 MVVM 框架。
Collor 用于并改进了 Voyages-sncf.com 应用。

特性

以下是所有特性的列表

  • 易于使用。
  • 可读的collectionView模型。
  • 设计为可重用cell。
  • 协议/结构面向。
  • 可扩展。
  • 决不使用IndexPath
  • 决不注册cell。
  • 轻松更新collectionView模型。
  • 选择数据或部分
  • 选择处理删除插入移动更新
  • 轻松管理自定义布局中的装饰视图。
  • 简化自定义布局的构建。
  • Swift 4(使用1.0.x以实现Swift 3兼容性)。
  • 经过充分测试。
  • 🆕 处理辅助视图(可能在下一个版本中改进)

Collor Random Sample Collor Weather Sample

开始使用

示例

要运行示例项目,请先克隆仓库,然后从示例目录运行pod install
这里有4个示例

  • 菜单:带有用户事件传播的简单collectionView示例
  • 随机:完整数据diffing +自定义布局
  • 天气:区域diffing +自定义布局
  • 潘通:使用CollectionDatas添加和删除项目。
  • 实时:复杂diffing(插入、删除、重新加载)+带有DecorationViewHandler的自定义布局处理。
  • 字母:一个带supplementaryView的示例,以及使用SupplementaryViewsHandler的自定义布局处理。

使用

UICollectionView由一个包含sectionDescriptors的collectionData对象表示,而sectionDescriptors又包含cellDescriptors。Collor中的每个条目或单元组成由3个对象

  • 实现CollectionCellAdaptableUICollectionViewCell(是否为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公开了添加补充视图的新方法。

Collor Alphabet Sample

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)

Collor Realtime Sample

获取更多信息,请查看这篇文章medium article

XCTemplates

Collor包装了3个XCTemplates,帮助您创建 ViewController、SectionDescriptor 和 CellDescriptor。

要安装它们,只需进入 xctemplates 目录,并在终端中运行以下命令:

sh install.sh

XCTemplates

需求

  • 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文件。