SimpleSource 3.0.2

SimpleSource 3.0.2

Morten HeibergAndrew Vyazovoy 维护。



  • Morten Heiberg 和 Thor Frolich

Build Status codecov.io License CocoaPods compatible

快速开始

概述?

SimpleSource是一个库,它可以让您轻松地填充和更新表格视图和集合视图。它提供了全类型闭包,因此您不必转换视图或项,它让您处理模型对象而不是索引路径,并且它负责处理增量更新中的单元格簿记。

运行示例程序。浏览UI,看看每个视图控制器中代码量有多少。然后回来这里学习更多。

$ cd Examples/
$ pod install
$ open SimpleSourceExample.xcworkspace

内容

介绍

永远不要再实现 UITableViewDataSourceUICollectionViewDataSource

SimpleSource是一个小型、专注于让您

  • 从手动管理的数组或Core Data填充和更新UITableViewUICollectionView视图。
  • 忘记dequeuing和单元格类型转换。忘记将IndexPath转换为模型对象。SimpleSource将直接为索引路径提供您所需的正确类型视图和模型对象。您可以专注于将自定义数据应用于自定义视图。
  • 忘记单元簿记。只需更改数据,SimpleSource将更新、添加和删除视图中的项目和服务。

是这样的吗?

这些都是主要功能,但是当然还有更多。

  • 自动差异和动画更新。将您的项目存储在常规的 Swift Array 中。只需重新分配或修改数组,正确的增量更改就会自动应用到底视图或集合视图中 - 以动画形式进出相应的行。Core Data 亦是如此。与 reloadData() 说再见。
  • 清晰地将表示逻辑与模型数据分离。SimpleSource DataSource 对象完全是关于模型数据。它一无所知,关于视图。甚至不知道是否会被用于驱动表或集合视图。或者它将为多少个视图提供数据。
  • 内置或自定义视图?您来决定。对于 UITableView,您可以为单元格使用任何内置的 UITableViewCellCellStyle。对于标题和页脚,您可以使用内置的基于文本的视图,只需提供要显示的字符串即可。当然,您也可以使用自定义视图作为单元格、标题和页脚。
  • 使用代码还是 Interface Builder 设计?您来决定。您告诉 SimpleSource 如何获取您的自定义视图。要么直接实例化一个类,要么加载 NIB,使用故事板原型(对于表视图)或使用内置单元格样式。
  • 轻松重新排列集合视图单元格。您希望在集合视图中的项目进行拖放吗?通过向 SimpleSource 提供一个可选的重新排序代理,您可以在不超过 1 行代码的情况下实现拖放式重新排序。查看示例项目。

在介绍了基本使用方法之后,本文档中还将有一些更高级的技巧和技巧。

概览

使用 SimpleSource 时有 3 个组件。为了填充表视图或集合视图,您需要每个组件各一个。

  • ViewDataSource – 这是您给 UIKit 的部分。它为您实现了 UITableViewDataSourceUICollectionViewDataSource。要创建其中一个,您需要一个 DataSource 和一个 ViewFactory
  • DataSource – 这是您的项目来源的地方。如果您想将它们组织在数组中,请使用 BasicDataSource。如果它们在 Core Data 中,请使用 CoreDataSource。 DataSource 完全不关心视图。
  • ViewFactory – 它负责创建和配置您的单元格。在创建 ViewDataSource 之前,您教 ViewFactory 如何从模型项创建和配置所有不同的视图。然后 ViewDataSource 将找到并将相关的模型项传递给 ViewFactory 以进行卸载队列和配置,最后将配置后的视图发送到UIKit进行显示。

总结

表格视图或集合视图会请求 ViewDataSource 为指定索引路径提供要显示的视图。使用此索引路径,ViewDataSourceDataSource 获取对应的模型对象,并将其交给 ViewFactory。然后,ViewFactory 取出一个单元,并使用模型对象配置视图,之后再将其交还给 ViewDataSource

我们将使用 ViewDataSourceViewFactoryDataSource 这三个术语来对这些组件进行一般性的描述。

Chart

根据您获取数据的方式(数组或 Core Data)以及您希望显示其的地方(表格或集合视图),每个组件类型都有几种不同的具体实现。

组件 类名
ViewDataSource TableViewDataSource / CollectionViewDataSource
ViewFactory TableViewFactory / CollectionViewFactory
DataSource BasicDataSource / CoreDataSource

SimpleSource 不是什么

SimpleSource 严格上是视图的数据源。具体来说,它不想成为您的视图的代理。任何与单元格/行选择、集合视图布局、行高度等有关的内容都由您和您的代理代码负责。

DataSource 不旨在替换或取代您的应用的数据持久层

  • CoreDataSource 只是围绕您从自己的数据库创建的 NSFetchedResultsController 的包装器。
  • BasicDataSource 只是围绕您从任何地方获取的项目的常规 Swift 数组的包装器。

我们还将巧妙的协议和泛型保持到最低限度。表格视图和集合视图固有地存在某些差异。我们接受这一点,并不试图抽象化一个单一的 API 来覆盖一切,这也与实际不符。您也不必是一位类型理论家才能在表格中显示项目数组。

有什么风险?

应该没有风险。没有人愿意放弃对不透明库的控制权。

使用 SimpleSource,每个动态部分要么是您提供的闭包,要么是一个易于更换的组件。该库相当小巧,大部分只是把不同的部分组合在一起,形成一个灵活、有效的整体。

随着您继续阅读本文档,您将了解如何支持自定义数据库、禁用或调整动画以满足您的需求等。

安装

CocoaPods

要将SimpleSource包含到使用CocoaPods的项目中,请将以下条目添加到您的Podfile:

pod 'SimpleSource'

然后运行命令pod install将SimpleSource添加到您的workspace。

入门指南

我们将构建一个简单的示例,展示按部门分组员工的表格。

数据

类似于UITableViewUICollectionView,SimpleSource是围绕将项目结构化的项目构建的。因此,我们的项目将包括员工,而我们的部分将包括他们所在的部门。

我们将使用简单的值类型和数组,因此对于当前的工作,BasicDataSource是合适的。

这是我们的员工对象

struct Employee {
    var name: String
}

接下来是部分,这些将是部门。在这一点上,任何符合SectionType协议的都可以作为部分。

部分只需提供提供一个items数组。但我们可以在部分中添加更多属性,例如一个标题(或任何我们需要的),以正确配置部分标题等。

为了说明这一点,让我们也添加部门名称,使模型稍微丰富一些。

struct Department: SectionType {
    typealias ItemType = Employee
    var name: String
    var items: [ItemType]
}

现在我们可以构建我们的数据集

// Employees
let alice = Employee(name: "Alice")
let bob = Employee(name: "Bob")
...

// Departments
let engineering = Department(name: "Engineering", items: [alice, christine, diana])
let sales = Department(name: "Sales", items: [bob, eliza, frank])
...

// Collect all departments
let departments = [engineering, sales, ...]

数据源

一旦我们有了数据,创建一个BasicDataSource就很简单

let dataSource = BasicDataSource(sections: departments)

请注意,dataSource.sections是一个可变数组的Department。对于每个部分,section.itemsEmployee的可变数组。

一旦一切运行正常,我们就可以修改这些数组,表格视图将自动更新并带有适当的动画。

视图工厂

通往工作表下一步骤是创建一个视图工厂。它将负责创建和配置单元格。

使用闭包创建一个ViewFactory,每次要出列新的单元格时都会调用。它返回单元格的重用标识符。

let viewFactory = TableViewFactory<Employee> { item, view in
    return "Cell"
}

提示:如果你的视图中有多种单元格类型,查看传给闭包的item(以我们的情况,item将是Employee类型)。然后决定使用哪种类型的单元格,并返回相应的重用标识符。

现在我们必须教会视图工厂使用"Cell"重用标识符要出列哪些单元格以及如何配置它们。这通过配置闭包来完成。

在这个简单案例中,我们使用纯UITableViewCell,所以闭包得到的就是它。但如果你有自定义的单元格子类,SimpleSource将把你的闭包发送给你。不需要类型转换。

let configureCell = { (cell: UITableViewCell, employee: Employee, indexPath: IndexPath) -> Void in
    cell.textLabel?.text = employee.name
}

viewFactory.registerCell(
    method: .style(.default),
    reuseIdentifier: "Cell",
    in: tableView,
    configuration: configureCell
)

技巧

如果你正在使用自定义单元格类,可以将配置闭包作为一个静态类变量存储在单元格上。然后将(例如)EmployeeCell.configureCell传递给registerCell。这种方法还可以用来存储重用标识符,例如,作为EmployeeCell.defaultReuseIdentifier

如果你使用尾随闭包语法,可以在调用registerCell时进行配置。

如果你的单元格配置闭包需要SimpleSource未提供的数据,你可以在创建闭包时捕获这些依赖项。在添加分区标题文本到我们的表中,你会看到这个例子。

为了保险起见,也让viewFactory为每个部门添加带有部门名称的文本标题。

viewFactory.registerHeaderText(in: tableView) { section in
    return dataSource.sections[section].name
}

注意配置闭包如何在这里捕获数据源并使用它来获取每个分区标题的部门名称。这是可以的,因为数据源不持有关于任何内容的强引用,只是模型对象。但为了避免保留周期,你应该小心不要捕获最终保留视图工厂的东西。在配置闭包中使用[weak ...]注释来断开任何保留周期。

视图数据源

现在我们已经准备好创建我们的表格视图的UITableViewDataSource。这将是一个TableViewDataSource的实例。

let tableViewDataSource = TableViewDataSource(
    dataSource: dataSource, 
    viewFactory: viewFactory, 
    viewUpdate: tableView.defaultViewUpdate()
)

这是我们需要连接dataSourceviewFactory的地方。

注意:有关对viewUpdate参数的解释,请参阅live view updates部分。

连接数据源

我们现在需要做的就是将tableViewDataSource连接到我们的表格视图

tableView.dataSource = tableViewDataSource

然后,我们的表就准备好了

Table

实时视图更新

我们还没有提到对数据源所做的更改是如何显示在视图中的。

视图数据源监听数据源的更改。这些更新可以来自提供给CoreDataSource,或者来自在BasicDataSource中重新分配节或项目数组时SimpleSource计算出的差异。

然后必须将这些更改应用到视图中。

创建视图数据源时,还会传递一个viewUpdate闭包,该闭包负责将增量更改合并到视图。

大多数情况下,你可能想使用内置行动画中的一种,并对收集视图使用performBatchUpdates

对于表格视图,SimpleSource定义了UITableView.defaultViewUpdate(),它为您执行此动画更新。如果您希望进行无动画更新,则可以使用UITableView.unanimatedViewUpdate。或者您可以创建自己的。它只是一个闭包。您还可以将您喜欢的UITableViewRowAnimation传递到defaultViewUpdate()以自定义它。

对于收集视图,内置的视图更新器称为UICollectionView.defaultViewUpdateUICollectionView.unanimatedViewUpdate。动画由收集视图布局提供。请参阅UIKit文档中的initialLayoutAttributesForAppearingItem(at:)和类似内容。

示例

Examples/目录中有一个游乐场和示例项目。

要尝试它,请运行以下命令

$ cd Examples/
$ pod install
$ open SimpleSourceExamples.xcworkspace

在这个项目中,您将看到如何使用基本数组和Core Data,如何创建自定义头部和尾部,视图如何自动更新,如何执行拖放收集视图重新排序以及更多。

注意:如果您想要尝试游乐场,请确保通过.xcworkspace文件打开它。这将允许它定位和构建必要的框架,以便它可以导入SimpleSource。

基础之外

集合视图重新排序

使用 SimpleSource,添加对集合视图单元格重新排序的支持可以实现得非常简单,只需要一行代码。

第一步是确保您的集合视图已经实现了正确的手势处理。这超出 SimpleSource 的范围,但请参阅 UICollectionViewinstallsStandardGestureForInteractiveMovement 属性文档。可以将此属性设置为 true 或安装自己的自定义手势。

CollectionViewDataSource 类有一个可选的 reorderingDelegate 属性,可以将其设置为启用单元格重新排序。

此重新排序代理由 CollectionViewReorderingDelegate 协议定义,它在重新排序完成后负责在 DataSource 中进行必要的修改。

对于 BasicDataSource,只需一行实际代码即可实现。

func reordering(collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
    dataSource.moveItem(at: sourceIndexPath, to: destinationIndexPath)
}

使用 CoreDataSource 实现时,需要修改数据,使位于 sourceIndexPath 的对象移动到 destinationIndexPath。如何操作取决于 Core Data 模型和 NSFetchedResultsController 的排序标准,该控制器是由 CoreDataSource 生成的。

请查看示例应用以获取单元格重新排序的演示。

更多技巧

  • 你喜欢 MVVM 吗? 我们也是!视图模型是放置 DataSource 的绝佳位置。然后保持 ViewFactoryViewDataSource 在视图层中。可以在您的视图控制器或辅助类中。
  • 带来自己的数据库。 简单来说,SimpleSource 在默认情况下提供了对手动管理的数组以及 Core Data(使用 NSFetchedResultsController)中存储的项目支持。如果您正在使用不同的数据库,通过遵循 DataSourceType 协议可以轻松编写自己的数据源。SimpleSource 的其余部分 - 单元格取回、视图更新、单元格配置等 - 设计为模块化,将与您的自定义数据源良好协同工作。
  • 一个数据源,多个视图。 由于 DataSource 对视图一无所知,因此如果需要,可以使用单个数据源驱动多个视图。
  • 自定义动画。 如果您需要特定的方法来将数据更新集成到视图中,可以编写自己的 viewUpdate 闭包并传递给 ViewDataSource
  • 使用多种单元格类型。如果在您的视图中需要显示多种单元格类型,请查看传递给ViewFactory闭包的item。然后决定使用哪种类型的单元格并返回相应的重用标识符。然后可以通过类型安全的闭包对每个单元格进行配置,该闭包获取特定单元格类型的实例以及要配置的项目。
  • 使用多种项目类型。如果您想使用多种单元格类型,那么您的数据源中的项目也可能有多种类型。它们可能不为共同的基础类或协议共享。在这种情况下,一个很好的解决方案是将它们包装在Swift的enum中。将每个项目/单元格类型作为您的enum中的一个case,并将项目作为与enum条目关联的值存储。

示例:想象一个支持不同类型首选项的典型应用设置屏幕。我们需要许多不同的单元格和项目类型,因此我们将每个类型的首选项定义为enum Preference中的case。比如说,ViewFactory闭包得到一个项目并看到它是Preference.boolean(name: String, value: Bool)。它知道要返回SwitchCell.reuseIdentifier。现在将回退一个SwitchCell的实例,可以使用关联的namevalue来设置标题标签和首选项的开关。

获取帮助

如果您认为SimpleSource中存在bug,请打开GitHub问题。

对于一般帮助,请首先查看示例应用。它几乎涵盖了整个公开API面,并且每个屏幕都是针对特定需求或用例。

如果示例应用没有回答您的问题,请在上Chuck Overflow提出问题并使用标记simplesource

贡献

非常欢迎以拉取请求的形式做出的贡献。

在您的第一个拉取请求可以合并到官方SimpleSource存储库之前,您将需要签署一个贡献者许可协议,允许Squarespace Inc.包含您的贡献。作为作者,您仍然保留对您的贡献的所有权利、标题和利益。

请注意,该项目是根据贡献者行为准则发布的。通过参与此项目,您同意遵守其条款。有关更多信息,请参阅CONTRIBUTING.md文件。

致谢

作者想要感谢并承认 Dan Thorpe 的 TaylorSource 作为 SimpleSource 的灵感来源。

尽管 SimpleSource 的项目范围、类层次结构和协议设计与 TaylorSource 相差甚远,但它影响了大量的早期设计决策。

许可

Apache 2.0 (摘要)

版权所有 (c) 2017, Squarespace, Inc.