测试已测试 | ✗ |
语言语言 | SwiftSwift |
许可证 | MIT |
发布最后发布 | 2017 年 11 月 |
SwiftSwift 版本 | 3.0 |
SPM支持 SPM | ✗ |
Maintained by Matthias Buchetics.
一个框架,用于简化 UITableView
数据源和单元 Cells 的设置和配置。它允许对 UITableViewDataSource
和(可选)UITableViewDelegate
进行安全的类型设置。《DataSource》还提供开箱即用的 diff 和动画删除、插入、移动和更改。
示例应用包括展示 DataSource 功能的示例。示例展示了各种使用案例,从简单字符串列表到更复杂的用例,例如动态表单的设置。
创建一个使用数据模型(Example
)进行配置的 CellDescriptor
(在此情况下,为 TitleCell
)的 DataSource
。
此外,我们还添加了一个处理 didSelect
的事件处理器,该处理器处理 UITableViewDelegate
的 didSelectRowAtIndexPath
方法。
let dataSource: DataSource = {
DataSource(
cellDescriptors: [
CellDescriptor<Example, TitleCell>()
.configure { (example, cell, indexPath) in
cell.textLabel?.text = example.title
cell.accessoryType = .disclosureIndicator
}
.didSelect { (example, indexPath) in
self.performSegue(withIdentifier: example.segue, sender: nil)
return .deselect
}
])
}()
)
接下来,将您的 dataSource
设置为 UITableView
的 dataSource
和 delegate
。
tableView.dataSource = dataSource
tableView.delegate = dataSource
接下来,创建并设置模型。别忘了调用 reloadData
。
dataSource.sections = [
Section(items: [
Example(title: "Random Persons", segue: "showRandomPersons"),
Example(title: "Form", segue: "showForm"),
Example(title: "Lazy Rows", segue: "showLazyRows"),
Example(title: "Diff & Update", segue: "showDiff"),
])
]
dataSource.reloadData(tableView, animated: false)
DataSource
还可以用来配置部分头部和尾部。类似于 CellDescriptors
,您可以定义一个或多个 SectionDescriptors
。
let dataSource: DataSource = {
DataSource(
cellDescriptors: [
CellDescriptor()
.configure { (person, cell, indexPath) in
cell.configure(person: person)
}
],
sectionDescriptors: [
SectionDescriptor<String>()
.header { (title, _) in
.title(title)
}
])
}()
部分头部和尾部可以有自定义视图(.view(...)
)或简单的标题(.title(...)
)。也支持诸如 heightForHeaderInSection
(headerHeight
)之类的委托方法。
如果你的数据模型实现了 Diffable
协议,则支持在两套数据之间进行 diff 和动画更改。
public protocol Diffable {
var diffIdentifier: String { get }
func isEqualToDiffable(_ other: Diffable?) -> Bool
}
diffIdentifier
是一个 String
标识符,它描述了两个模型是否具有不同的身份。将其视为数据库中的主键。不同的 diffIdentifiers
将导致动画插入、删除或移动更改。此外,可以使用 isEqualToDiffable
来描述即使 diffIdentifier
相同,模型的数据或内容是否已经更改。例如,如果人的名字在数据库中被更改,该人的主键通常保持不变。在这种情况下,通常不希望插入、删除或移动,而是更新相应的表格行(可能带有动画)。
差分示例由以下示例展示
RandomPersonsViewController
在两个部分中创建一组随机的人,并动画地更改数据集之间的变化。
private func randomData() -> [SectionType] {
let count = Int.random(5, 15)
let persons = (0 ..< count).map { _ in Person.random() }.sorted()
let letters = Set(["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L"])
let firstGroup = persons.filter {
$0.lastNameStartsWith(letters: letters)
}
let secondGroup = persons.filter {
!$0.lastNameStartsWith(letters: letters)
}
return [
Section("A - L", items: firstGroup),
Section("M - Z", items: secondGroup),
]
}
@IBAction func refresh(_ sender: Any) {
dataSource.sections = randomData()
dataSource.reloadData(tableView, animated: true)
}
DiffViewController
通过行数据创建行的数字,其中 diffIdentifier
是该数字本身,而内容是该数字的英语或德语名称。这展示了如何实现动画行的变化。
struct DiffItem {
let value: Int
let text: String
let diffIdentifier: String
init(_ value: Int, text: String) {
self.value = value
self.text = text
self.diffIdentifier = String(value)
}
}
extension DiffItem: Diffable {
public func isEqualToDiffable(_ other: Diffable?) -> Bool {
guard let other = other as? DiffItem else { return false }
return self.text == other.text
}
}
请参考示例代码的全文。
两者,CellDescriptor
和 SectionDescriptor
都提供了一个 isHidden
封闭,可以根据任何自定义条件简单地隐藏和显示行。
在 FormViewController
示例中,当姓氏字段不为空时,仅显示姓氏字段,并在一个开关启用时显示“附加字段”部分。
lazy var dataSource: DataSource = {
DataSource(
cellDescriptors: [
TextFieldCell.descriptor
.isHidden { (field, indexPath) in
if field.id == self.lastNameField.id {
return self.firstNameField.text?.isEmpty ?? true
} else {
return false
}
},
SwitchCell.descriptor,
TitleCell.descriptor,
],
sectionDescriptors: [
SectionDescriptor<Void>("section-name")
.headerHeight { .zero },
SectionDescriptor<Void>("section-additional")
.header {
.title("Additional Fields")
}
.isHidden {
!self.switchField.isOn
}
])
}()
每次调用 dataSource.reloadData(...)
时,都会计算 isHidden
封闭。
DataSource
通过使用闭包在类型安全和简单的方式下处理所有 UITableViewDelegate
方法,提供了一个方便的接口。在大多数情况下,你可以在 CellDescriptor
或 SectionDescriptor
上定义这些闭包。然而,有时这会导致代码重复,例如,如果你有不同的单元格,但针对选择的代码执行是相同的。在这种情况下,你可以在 DataSource
本身设置委托闭包。
dataSource.didSelect = { (row, indexPath) in
print("selected")
return .deselect
}
如果 CellDescriptor
(或 SectionDescriptor
)上没有定义特定委托方法的闭包,则这些闭包将用作回退。
此外,您还可以设置回退的 UITableViewDelegate
和 UITableViewDataSource
,在 CellDescriptor
或 SectionDescriptor
上未设置对应闭包时使用。
dataSource.fallbackDelegate = self
dataSource.fallbackDataSource = self
使用这些回退机制,您可以选择在特定用例中希望使用哪些部分的 DataSource
。例如,您可以使用它来设置和配置所有单元格,在数据集之间进行动画变化,但保持您现有的 UITableViewDelegate
代码。
可以通过 fallbackDelegate
使用来实现不属于 DataSource
的方法,例如 UIScrollViewDelegate
方法。应格外注意回退委托必须在设置表视图委托之前设置,否则某些委托方法将永远不会被 UIKit
调用。
// Always set the fallback before setting the table view delegate
dataSource.fallbackDelegate = self
tableView.dataSource = dataSource
tableView.delegate = dataSource
可以使用自定义包注册单元格。您可以在单元格描述符中指定从哪个包加载单元格。默认为主包。
let descriptor = CellDescriptor(bundle: customBundle)
当前 Swift 兼容性分解
Swift 版本 | 框架版本 |
---|---|
4.x | 5.x |
3.x | 3.x, 4.x |
将以下行添加到您的 Cartfile。
github "mbuchetics/DataSource", ~> 5.0
然后运行 carthage update
。
对于 DataSource,使用以下 Podfile 中的条目
pod 'MBDataSource'
然后运行 pod install
。
在任何您想要使用 DataSource 的文件中,别忘了使用 import DataSource
导入框架。
只需将 DataSource
文件夹中的 .swift
文件拖放到您的项目中即可。
您可以通过 matthias.buchetics.com 或在 Twitter 上关注我与我联系。