2.0.0 版本的 TaylorSource

TaylorSource 2.0.0

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布最新版本2015 年 9 月
SPM支持 SPM

Dan Thorpe 维护。



Taylor Source

Taylor Source 是一个 Swift 框架,用于创建高度可配置且可重用的数据源。

安装

Taylor Source 通过 CocoaPods 提供。要安装它,只需将以下行添加到您的 Podfile 中

pod ’TaylorSource

使用

Taylor Source 定义了一个 DatasourceType 协议,它有一个关联的 FactoryType 协议。这两个协议是框架的构建块。每个都具有基本的具体实现类 StaticDatasourceBasicFactory,并设计为易于实现。

基础

工厂负责注册和管理视图。它是一个泛型协议,允许您配置 cell & 视图类型等的基本类型。

数据源由工厂和数据初始化,可以用作生成 UITableViewDataSource 等。

我们建议在自定义类中组合 datasource。例如,假设有一个模型类型 Event,自定义 datasource 的类型如下

class EventDatasource: DatasourceProviderType {
  typealias Factory = BasicFactory<Event, EventCell, EventHeaderFooter, UITableView>
  typealias Datasource = StaticDatasource<Factory>

  let datasource: Datasource
}

配置 Cells

配置 cells 通过提供与 cell 类型相关的闭包和键进行描述。怎么了?来看看。

闭包类型接收三个参数,cell、模型实例和项的索引。在基本工厂中,这是 NSIndexPath。但它可以自定义。

cell 用以下任一方式描述:重用标识符和类

.ClassWithIdentifier(EventCell.self, EventCell.reuseIdentifier)

或重用标识符和 nib

.NibWithIdentifier(EventCell.nib, EventCell.reuseIdentifier)

(请参阅 ReusableViewResuableViewDescriptor

键是一个字符串,用于将配置闭包与 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组件的通用接口 - 用于你的所有查询。

YapDBFactory单元格和视图配置

提供单元格(和补充视图)配置闭包的索引类型是可以定制的。对于BasicFactory,这些是两者都是NSIndexPath。但对于YapDBFactory,单元格索引类型是一个结构体,它不仅提供了一个NSIndexPath,还提供了一个YapDatabaseReadTransaction。这意味着可以在闭包中从数据库读取配置单元格所需的任何其他对象。

对于补充视图配置块,索引类型进一步有了为当前分区定义的YapDatabaseView的组。通常,这是定义补充视图“模型”的最佳方式 - 通过将组合作为一个可从数据库使用读取事务读取的其他类型的标识符。

多个单元格和视图类型

有时可能需要在同一屏幕上拥有不同的单元格设计。或许还有不同的补充视图。这可以通过将每个单元格和视图注册到数据源工厂来实现。但是也有一些注意事项。

数据源的工厂实现了FactoryType,它定义了泛型类型CellTypeSupplementaryViewType。当一个设计中包含一个以上的单元格类(或补充视图类)时,这个泛型类型成为了公共父类。

对于表视图设计,所有单元格都继承自UITableViewCell,所以对于两个单元格子类,例如EventCellReminderCell,上面的示例定义将会是

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的表格视图删除行的工作方式。

设计目标

  1. 遵循D.R.Y.原则 - 我再也不想实现 func tableView(_: UITableView, numberOfRowsInSection: Int) -> Int 了。
  2. 简单地将数据源类型与视图控制器解耦。
  3. 支持自定义单元格和补充视图类。
  4. 使用类型安全闭包来定制单元格和视图。
  5. 轻松组合和扩展数据源类型。

贡献

在TaylorSource的API稳定(Swift 2.0之后)之前,我不寻求代码贡献。对于改进文档、拼写错误、单元测试和集成辅助的pull request形式的贡献始终欢迎。

作者

Daniel Thorpe - @danthorpe

许可协议

Taylor Source在MIT许可协议下提供。有关更多信息,请参阅LICENSE文件。