Functional Table Data 为 UITableView 提供了一种函数式的渲染方式。你只需传入表格状态的完整描述,Functional Table Data 就会将其与之前的渲染调用进行比较,从而插入、更新和移除已更改的 section 和 cell。这大大简化了复杂 UI 的状态管理。
你不再需要手动跟踪 UI 的 section 数量、cell 数量和索引。只需构建一个方法,从你的数据生成表格状态结构即可。提供的 HostCell
泛型可以轻松地将 FunctionalTableData 支持添加到 UITableViewCell
中。
值得注意的特性 | |
---|---|
💯 | 用于维护表格状态的函数式方法 |
👷 | 可重用的视图和状态 |
✅ | 单元测试 |
🔀 | 自动进行状态差异比较 |
❤️ | 在 Shopify 的 iOS 应用中广泛使用 |
🙅 | 不再需要 IndexPath 簿记 |
只需将 FunctionalTableData/FunctionalTableData.xcodeproj
拖放到你的 Xcode 项目中即可。
将 https://github.com/Shopify/FunctionalTableData
添加到你的软件包依赖项中。
将以下内容添加到你的 Cartfile
github "Shopify/FunctionalTableData"
要使用 Functional Table Data (FTD),需要两样东西:一个 UITableView 实例和一个 FTD 实例。一旦两者都可用,通常在 view controller 的 viewDidLoad
中,它们通过 functionalTableData.tableView = yourTableViewInstance
连接在一起。此后,每次我们想要显示/更新数据时,只需调用 functionalTableData.renderAndDiff(sections)
。
有关更多信息,请阅读我们的 文档。
任何时候你想要更新当前显示的数据,你都要生成新的状态并将其传递给你的 Functional Table Data 实例。FTD 然后负责计算先前状态和下一个状态之间的差异,并根据需要更新自身。
FunctionalTableData
持有一个 section 数组,其中每个 section 都有一个 key。此 key 必须在所有 section 中是唯一的,但应该是确定性的,以便可以在不替换整个 section 本身的情况下调整该 section 中包含的行。
let section = TableSection(key: "header-unique-key", rows: [])
每个 section 包含一系列行,其中每个行值必须符合 CellConfigType
协议。
/// The simplest possible version of a cell that displays a label.
/// Useful to get started, but in most cases a more robust state should be used allowing more customization.
typealias LabelCell = HostCell<UILabel, String, LayoutMarginsTableItemLayout>
let cells: [CellConfigType] = [
LabelCell(key: "company", state: "Shopify") { view, state in
view.text = state
},
LabelCell(key: "location", state: "🇨🇦") { view, state in
view.text = state
}
]
这些行本身也有一个 key,该 key 在该 section 中必须是唯一的。此 key 用于确定何时将新行添加到 section,是否移除了任何行,或者是否将任何行移动到不同的位置。此外,每种 CellConfig
类型都实现一个 isEqual 函数,以确定它们中的两个是否表示正在显示的相同数据。 这允许单个 cell 执行状态更改操作,即,从 off
切换到 on
状态,文本值更改等。
将变量 rows
分配给我们之前创建的 section
后,在 table view 中显示数据所需的只是此方法。
functionalTableData.renderAndDiff([section])
知道 cell 由视图和状态组成,让我们从一个简单的例子开始,一个显示标签的 cell。通过指定 HostCell
的泛型要求,最简单的例子是使用 UILabel
作为其视图,String
作为其状态,以及 LayoutMarginsTableItemLayout
作为布局 (有关更多信息,请参阅 TableItemLayout
)。
typealias LabelCell = HostCell<UILabel, String, LayoutMarginsTableItemLayout>
// Usage
LabelCell(key: "company", state: "Shopify") { view, state in
view.text = state
}
虽然之前的代码对于入门非常有用,但在大多数情况下,应该使用更健壮的状态,允许更多的自定义。一个更好的例子可能是这样的:
typealias LabelCell = HostCell<UILabel, LabelState, LayoutMarginsTableItemLayout>
struct LabelState: Equatable {
let text: String
let alignment: NSTextAlignment
let color: UIColor
static func ==(lhs: LabelState, rhs: LabelState) -> Bool {
return lhs.text == rhs.text && lhs.alignment == rhs.alignment && lhs.color == rhs.color
}
}
// Usage
LabelCell(key: "company", state: LabelState(text: "Shopify",
alignment: .center,
color: .green)) { view, state in
guard let state = state else {
// If the state is `nil`, prepare this view to be reused
view.text = ""
view.textAlignment = .natural
view.textColor = .black
return
}
view.text = state.text
view.textAlignment = state.alignment
view.textColor = state.color
}
归根结底,HostCell
只是 CellConfigType
的一种可能的实现,这就是这个框架的底层强大之处。
在野外看到了其他文章? 欢迎打开 pull request。
Functional Table Data 采用 MIT 许可证