Taylor Source 是一个 Swift 框架,用于创建高度可配置且可重用的数据源。
Taylor Source 通过 CocoaPods 提供。要安装它,只需将以下行添加到您的 Podfile 中
pod ’TaylorSource’
Taylor Source 定义了一个 DatasourceType
协议,它有一个关联的 FactoryType
协议。这两个协议是框架的构建块。每个都具有基本的具体实现类 StaticDatasource
和 BasicFactory
,并设计为易于实现。
工厂负责注册和管理视图。它是一个泛型协议,允许您配置 cell & 视图类型等的基本类型。
数据源由工厂和数据初始化,可以用作生成 UITableViewDataSource
等。
我们建议在自定义类中组合 datasource。例如,假设有一个模型类型 Event
,自定义 datasource 的类型如下
class EventDatasource: DatasourceProviderType {
typealias Factory = BasicFactory<Event, EventCell, EventHeaderFooter, UITableView>
typealias Datasource = StaticDatasource<Factory>
let datasource: Datasource
}
配置 cells 通过提供与 cell 类型相关的闭包和键进行描述。怎么了?来看看。
闭包类型接收三个参数,cell、模型实例和项的索引。在基本工厂中,这是 NSIndexPath
。但它可以自定义。
cell 用以下任一方式描述:重用标识符和类
.ClassWithIdentifier(EventCell.self, EventCell.reuseIdentifier)
或重用标识符和 nib
.NibWithIdentifier(EventCell.nib, EventCell.reuseIdentifier)
(请参阅 ReusableView
和 ResuableViewDescriptor
)
键是一个字符串,用于将配置闭包与 cell 关联。将所有这些放在一起意味着注册和配置 cells 与以下代码一样简单
datasource.factory.registerCell(.ClassWithIdentifier(EventCell.self, EventCell.reuseIdentifier), inView: tableView, withKey: “Events”) { (cell, event, indexPath) in
cell.textLabel!.text = “\(event.date.timeAgoSinceNow())”
}
尽管推荐将配置块作为您自定义 cell 类型上的类函数提供。有关示例,请参阅示例项目。
补充视图是页眉和页脚,尽管UICollectionView
允许自定义。这都得到了支持,但是页眉和页脚有方便的注册方法。
除了补充视图的kind
,它们的函数就像单元格一样,但是每个kind
只能注册一个闭包。换句话说,相同的闭包将配置所有你的表视图分区标题。
基本功能只支持静态不可变数据。因为如果你需要更多,最好使用数据库。本仓库中的示例项目灵感来源于苹果默认的Core Data模板,但适用于YapDatabase。开始
pod ’TaylorSource/YapDatabase’
这使得新的Factory和Datasource类型可用。早期的专用数据源将变成
class EventDatasource: DatasourceProviderType {
typealias Factory = YapDBFactory<Event, EventCell, EventHeaderFooter, UITableView>
typealias Datasource = YapDBDatasource<Factory>
let datasource: Datasource
}
YapDBDatasource
实现了DatasourceType
,除了它从提供的YapDatabase
实例获取数据。内部,它使用YapDatabase的视图映射,这些映射由数据库Observer
配置和组合。它可以与YapDatabase的视图、过滤视图和搜索一起使用。我强烈建议你阅读YapDatabase的维基页面。但是,简单来说,视图就像保存的数据库查询,执行过滤、排序和模型项映射。YapDBDatabase
然后提供用于UI组件的通用接口 - 用于你的所有查询。
提供单元格(和补充视图)配置闭包的索引类型是可以定制的。对于BasicFactory
,这些是两者都是NSIndexPath
。但对于YapDBFactory
,单元格索引类型是一个结构体,它不仅提供了一个NSIndexPath
,还提供了一个YapDatabaseReadTransaction
。这意味着可以在闭包中从数据库读取配置单元格所需的任何其他对象。
对于补充视图配置块,索引类型进一步有了为当前分区定义的YapDatabaseView
的组。通常,这是定义补充视图“模型”的最佳方式 - 通过将组合作为一个可从数据库使用读取事务读取的其他类型的标识符。
有时可能需要在同一屏幕上拥有不同的单元格设计。或许还有不同的补充视图。这可以通过将每个单元格和视图注册到数据源工厂来实现。但是也有一些注意事项。
数据源的工厂实现了FactoryType
,它定义了泛型类型CellType
和SupplementaryViewType
。当一个设计中包含一个以上的单元格类(或补充视图类)时,这个泛型类型成为了公共父类。
对于表视图设计,所有单元格都继承自UITableViewCell
,所以对于两个单元格子类,例如EventCell
和ReminderCell
,上面的示例定义将会是
class EventCell: UITableViewCell { }
class ReminderCell: UITableViewCell { }
class EventDatasource: DatasourceProviderType {
// Note that cell is common parent class.
typealias Factory = YapDBFactory<Event, UITableViewCell, EventHeaderFooter, UITableView>
typealias Datasource = YapDBDatasource<Factory>
let datasource: Datasource
}
在某些情况下,使用基类单元格效率更高,例如,ReminderCell
实际上可能是一种特殊的EventCell
。
class EventCell: UITableViewCell { }
class ReminderCell: EventCell { }
class EventDatasource: DatasourceProviderType {
// Note that here EventCell is our common parent cell.
typealias Factory = YapDBFactory<Event, EventCell, EventHeaderFooter, UITableView>
typealias Datasource = YapDBDatasource<Factory>
let datasource: Datasource
}
对于补充视图也是同样的规则适用,例如分区标题和页脚,它们对于表视图继承自UITableHeaderFooterView
,而对于收集视图继承自UICollectionReusableView
。
注册单元格需要包含它的视图,例如UITableView
实例。因此,推荐的最佳实践是在表格视图中初始化自定义数据源提供者。以下是如何使用两个单元格子类示例的说明:
class EventDatasource: DatasourceProviderType {
typealias Factory = YapDBFactory<Event, UITableViewCell, EventHeaderFooter, UITableView>
typealias Datasource = YapDBDatasource<Factory>
let datasource: Datasource
init(db: YapDatabase, view: Factory.ViewType) {
// Create a factory. This closure is discussed below.
let factory = Factory(cell: { (event, index) in return event.isReminder ? "reminder" : "event" } )
// Create the datasource
datasource = Datasource(
// this can be whatever you want, to help debugging.
id: "Events datasource",
// the YapDatabase instance - see YapDB from YapDatabaseExtensions.
database: db,
// the factory we defined above.
factory: factory,
// provided by TaylorSource to auto-update from YapDatabase changes.
processChanges: view.processChanges,
// See example code for info on creating YapDB Configurations. Essentially this is a database fetch request for Event objects.
configuration: eventsConfiguration()
)
// Register the cells
datasource.factory.registerCell(
// See ReusableView in TaylorSource.
.ClassWithIdentifier(EventCell.self, EventCell.reuseIdentifier),
inView: view,
// The key used to look up the correct cell. See discussion below.
withKey: "event",
// This is a static fnction which returns a closure. See below.
configuration: EventCell.configuration()
)
datasource.factory.registerCell(
.NibWithIdentifier(ReminderCell.nib, ReminderCell.reuseIdentifier),
inView: view,
withKey: "reminder",
configuration: ReminderCell.configuration()
)
}
}
这要求这两个单元格类都实现ReusableView
并返回一个配置块。这是使用TaylorSource的特有功能,单元格的配置在单元格本身的静态闭包中定义,这使得它们与视图控制器解耦,提高了可用性。
工厂将出售单元格的一个实例。因此,在配置闭包内,需要进行强制类型转换。例如:
class EventCell: UITableViewCell {
@IBOutlet var iconView: UIImageView!
class func configuration() -> EventsDatasource.Datasource.FactoryType.CellConfiguration {
/* The `cell` constant here is typed as EventsDatasource.Datasource.FactoryType.CellType,
which in this example is UITableViewCell because we also have ReminderCell registered. */
return { (cell, event, index) in
cell.textLabel!.text = “\(event.date.timeAgoSinceNow())”
if let eventCell = cell as! EventCell {
eventCell.iconView.image = event.icon.image
}
// Note that we are only concerned with configuring the EventCell here.
}
}
}
上面的例子概述了一个由工厂初始化的闭包。在相同容器中使用多个单元格类时,这是一个关键细节。在这种情况下,一些单元格是一个设计,其他单元格是另一个设计。此切换逻辑通过在初始化时传递给TaylorSource工厂类(它是FactoryType
的基本实现)的闭包提供。这些闭包的格式如下:
Factory.GetCellKey = (Item, CellIndexType) -> String
Factory.GetSupplementaryKey = (SupplementaryIndexType) -> String
对于单元格,这意味着闭包将接收模型项及其在数据源中的索引。对于YapDBDatasources
,这意味着索引将是一个结构,提供NSIndexPath
以及YapDatabaseReadTransaction
。闭包应使用此信息,并返回一个String
,用作单元格的查找键。在我们上面的例子中,Event
模型具有一个isReminder
布尔属性。
闭包返回的键在注册对应的单元格时用作withKey
参数。
查看Gallery示例项目,了解如何在同一数据源中使用多种单元格样式。
如果需要具有多个不同单元格(每个单元格与其自己的模型相关联)的表格视图(或集合视图),请使用枚举来包装相应的模型。例如,对于上面的例子,假设EventCell
需要一个Event
模型,而ReminderCell
需要一个Reminder
模型。我们如何将这些放入数据源中?
struct Event {}
struct Reminder {}
enum CellModel { // but come up with a better name!
case Event(ModuleName.Event) // Use full name definition to avoid clashes.
case Reminder(ModuleName.Reminder)
}
extension CellModel {
static var getCellKey: EventDatasource.Datasource.FactoryType.GetCellKey {
return { (item, _) in
switch item {
case .Event(_): return "event"
case .Reminder(_): return "reminder"
}
}
}
}
class EventDatasource: DatasourceProviderType {
typealias Factory = YapDBFactory<CellModel, UITableViewCell, EventHeaderFooter, UITableView>
typealias Datasource = YapDBDatasource<Factory>
let datasource: Datasource
init(db: YapDatabase, view: Factory.ViewType) {
// Create a factory. Use the closure as defined on the cell model.
let factory = Factory(cell: CellModel.getCellKey)
// etc
}
}
Apple的UITableViewDataSource
有一些可选方法,支持在编辑模式下的表格视图。一般来说,编辑表格视图意味着插入、删除或移动行。
TaylorSource通过在DatasourceProviderType
上定义可选闭包来支持此功能。为了启用从TableViewDatasourceProvider
生成的UITableViewDataSource
的可选方法,必须提供所有四个闭包,如果需要,可以使用空的实现。
查看Events示例项目,了解从后端为YapDatabase
的表格视图删除行的工作方式。
func tableView(_: UITableView, numberOfRowsInSection: Int) -> Int
了。在TaylorSource的API稳定(Swift 2.0之后)之前,我不寻求代码贡献。对于改进文档、拼写错误、单元测试和集成辅助的pull request形式的贡献始终欢迎。
Daniel Thorpe - @danthorpe
Taylor Source在MIT许可协议下提供。有关更多信息,请参阅LICENSE文件。