Collor:UICollectionView的面向MVVM数据的框架

问题

在oui.sncf(法国和欧洲的火车票预订主要应用程序)上,我们的主要工作是显示服务器发送给我们的内容,大多数视图也是动态的。 此外,该应用程序经常会更新为新功能,甚至静态视图也会不断发展。

因此,我们决定使用UICollectionView! 它功能强大,动态且可自定义。

但是,当有很多不同的项目时,例如在Voyages-sncf.com应用程序中,Apple的面向indexPath的实现可能会变得混乱。
代码重复,切换大小写,难以维护,更新期间存在indexPath错误的风险……

我们已经建立了一年的框架,以简化和加快这些UICollectionView屏幕的开发。 该库的主要目标是在一个文件中拥有一个可读的dataSource,它表示collectionView内容。

我们将其称为CollorColl [ ectionViewDescript]

https://github.com/voyages-sncf-technologies/Collor

科洛尔做了两件事:

  • 首先,它提供了基于MVVM的可伸缩微体系结构,以组织代码并避免重复。
  • 然后,它提供了一些功能,以便删除collectionView实现所需的许多代码,例如单元格注册等,并轻松更新collectionView:删除,插入,重新加载,比较等。

建筑

为了描述collectionView,一个collectionData对象,它继承自CollectionData 用来。 它分为部分和项目。

UICollectionView数据源要求collectionData知道节的数量,节中的项目数以及要出队的单元格。

collectionData对象包含一个SectionDescriptor数组:SectionDescriptor实现协议SectionDescribable ,并使用Apple的FlowLayout处理某些节功能,例如sectionInsetminimumInteritemSpacingminimumLineSpacing 。 SectionDescriptor还包含一个CellDescriptor数组。

例如,在Voyages-sncf.com应用程序上,每次我们需要一个简单的标签单元时,我们只需创建一个新适配器即可实现VSCollectionLabelAdapter协议, VSCollectionLabelAdapter用UICollectionViewCell及其先前创建的描述符。

适配器管理标签的样式; 标签会使用Autolayout填充整个单元格,并使用NSAttributedString.boundingRect()计算其高度

一些代码来解释我们做了什么:

确实,您可能有一个描述符,该描述符以最小的变化处理相似的单元:左侧或右侧的图像。 因此将有两个单元,但是一个描述符和一个公共适配器协议。

Collor是一个面向协议的框架。 可以在示例中实现新协议BackgroundDrawable ,而不是在节描述符中添加属性,因此可以在其他自定义布局中重用…

 协议BackgroundDrawable { 
var backgroundInset:UIEdgeInsets {获取设置}
}

CollectionView更新

如果您已经必须在tableView或collectionView中实现扩展/折叠,那么Collor应该会让您感兴趣。

实际上, Collor提供了一些轻松添加,删除,重新加载节或项目的方法。 不再需要使用IndexPath ,您只需操纵描述符引用, Collor即可完成工作。 更新collectionData之后, Collor通过调用UICollectionView.performBatchUpdates(_:completion:)UICollectionView.performUpdates(with:completion:)为您提供一个用于更新collectionView的结果对象。 数据和视图也始终与collectionView同步。

 让result = myCollectionData.update { 
updater.remove(cells:[cellDescriptor])
updater.append(sections:[blueSection],之后:lastSection!)
// ...
}
collectionView.performUpdates(带有:结果)

collectionData中的每个更新都必须包含在CollectionData.update(_:)给出的闭包中

看看github上的pantone示例。

差异化

差异数据

使用Collor ,可以计算两个数据之间的差异。 Collor正在使用Jack Flintermann令人惊叹的Dwifft库来完成该任务。 如果您的数据包含条件显示,这将非常有用。

用几行代码计算两个数据状态之间的差异:

  myModel.someChanges() 
yourCollectionData.update(model:myModel)
让结果= yourCollectionData.update {更新
updater.diff()
}
collectionView.performUpdates(带有:结果)

要使用此功能,每个部分和每个项目都必须具有唯一的标识符,即字符串值:

 让yellowSection = MainColorSectionDescriptor()。uid(“ yellowSection”) 
让yellowTitle = TitleDescriptor(适配器:TitleAdapter(颜色:.yellow))。uid(“ yellowTitle”)

不同部分中的单元格可以具有相同的id,在计算差异时,单元格uid和其部分uid串联在一起。 在上一个示例中,yellowTitle uid是yellowSection / yellowTitle。

看看github上的随机样本。

区分部分

您可以在一个部分中进行一些更改。 例如,展开崩溃。 计算整个数据的差异毫无意义。

Collor提供了一项功能,可以仅计算某些部分中的差异:

  sectionDescriptor.isExpanded =!sectionDescriptor.isExpanded 
让结果= collectionData.update {更新
updater.diff(sections:[sectionDescriptor])
}
collectionView.performUpdates(带有:结果)

由于此功能,每个部分建筑物都被封闭在一个封闭中。 Collor也可以具有sectionDescriptor单元格数组的旧状态和新状态。

  let section = MySectionDescriptor()。reloadSection { 
cells.append(...)
}

看看github上的天气样本。

感谢您的阅读,并让我知道您在Twitter上的想法。