AEViewModel
Swift 实用工具,方便创建表格和集合视图
索引
简介
几乎所有应用程序的界面都常常在表格或集合视图中。这是我为自身简化这项任务的一些尝试,希望能为其他人提供帮助。
此解决方案背后的理念相当抽象,这意味着它可以以许多不同(或风格)的方式应用于多个场合或喜好。
通过适当使用此框架,它将强制您通过利用 MVVM 模式的概念来编写更干净和可维护的代码。为了更好地理解它,您首先应该熟悉 “手动”方法。
它可能不是快速和容易(对于每个人)就能掌握的,但如果你给它机会,你可能永远不会想在不使用它的情况下创建另一个表格或集合视图...
功能
- 更快地创建自定义表格/集合界面,减少样板代码
- 适用于静态(菜单、表单等)和动态(本地、远程等)数据
- 提供常用单元格(文本输入、滑块、开关、按钮等)
使用方法
数据源
建议首先熟悉 DataSource.swift,因为你会用这些功能做几乎所有的事情。
这些都是从 DataSource
开始的非常简单的协议,必须具有部分,然后每个 Section
必须包含项目,其中每个 Item
包含 identifier: String
、viewModel: ViewModel
和可选的 child: DataSource?
。
/// ViewModel is whatever you choose it to be, easy like this:
struct MyCustomWhatever: ViewModel {}
基本数据源
在 BasicDataSource.swift 中有符合所有这些协议的简单结构,大多数情况下可以使用这些结构。由于这些结构也符合 Codable
,还可以从 JSON 数据等创建 BasicDataSource
。
对于更具体的功能,创建符合这些协议的自定义类型,并使用这些类型替代。
单元格
在 Cell.swift 中有一个简单的协议,用于表格和集合视图单元格。请注意,TableCell
和 CollectionCell
仅是简单的类型别名
public typealias TableCell = UITableViewCell & Cell
public typealias CollectionCell = UICollectionViewCell & Cell
创建自定义单元格时,最简单的方法是从 TableCellBasic
或 CollectionCellBasic
继承,并覆盖该协议的这些方法
/// Called in `init` and `awakeFromNib`, configure outlets and layout here.
func configure()
/// Called in `configure` and `prepareForReuse`, reset interface here.
func reset()
/// Called in `tableView(_:cellForRowAt:)`, update interface with view model here.
func update(with item: Item)
/// Called in `tableView(_:didSelectRowAt:)` and whenever specific cell calls it (ie. toggle switch).
/// By default this call will be forwarded to `delegate` (after setting some `userInfo` optionally).
/// If needed, call this where it makes sense for your cell, or override and call `super` at some moment.
func callback(_ sender: Any)
TableCell
提供了几个常用的表格视图单元格。
public enum TableCellType {
case basic
case subtitle
case leftDetail
case rightDetail
case toggle
case toggleWithSubtitle
case slider
case sliderWithLabels
case textField
case textView
case button
case spinner
case customClass(TableCell.Type)
case customNib(TableCell.Type)
}
CollectionCell
而对于集合视图单元格,你可能希望创建一些更定制化的内容... :)
public enum CollectionCellType {
case basic
case button
case spinner
case customClass(CollectionCell.Type)
case customNib(CollectionCell.Type)
}
TableViewController
这个故事的最后部分是TableViewController
,正如你所猜的,它继承自UITableViewController
。
唯一足够好的是,只需配置其dataSource
属性并重写这些方法,就可以注册、预分配和更新你需要的所有单元格。
/// - Note: Return proper cell type for the given item identifier.
/// Based on this it knows which cells to register for which identifier.
open func cellType(forIdentifier identifier: String) -> TableCellType {
return .basic
}
/// - Note: Update cell at the given index path.
/// `TableViewController` does this by default, so if that's enough for your case just skip this,
/// otherwise call `super.update(cell, at: indexPath)` and add custom logic after that.
open func update(_ cell: TableCell, at indexPath: IndexPath) {
let item = viewModel.item(at: indexPath)
cell.update(with: item)
cell.delegate = self
}
/// - Note: Handle action from cell for the given index path.
/// This will be called in `tableView(_:didSelectRowAt:)` or when `callback(_:)` is called
open func action(for cell: TableCell, at indexPath: IndexPath, sender: Any) {}
CollectionViewController
这几乎与TableViewController
相同,但它使用的是CollectionCell
。
示例
你应该参考示例项目,但这里有一个简短预览。
import AEViewModel
struct ExampleDataSource: DataSource {
struct Id {
static let cells = "cells"
static let form = "form"
static let settings = "settings"
static let github = "github"
}
var title: String? = "Example"
var sections: [Section] = [
BasicSection(footer: "Default cells which are provided out of the box.", items: [
BasicItem(identifier: Id.cells, title: "Cells")
]),
BasicSection(header: "Demo", items: [
BasicItem(identifier: Id.form, title: "Form", detail: "Static Data Source"),
BasicItem(identifier: Id.settings, title: "Settings", detail: "JSON Data Source"),
BasicItem(identifier: Id.github, title: "Github", detail: "Remote Data Source")
])
]
}
final class ExampleTVC: TableViewController {
typealias Id = ExampleDataSource.Id
// MARK: Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
dataSource = ExampleDataSource()
}
// MARK: Override
override func cellType(forIdentifier identifier: String) -> TableCellType {
return .subtitle
}
override func update(_ cell: TableCell, at indexPath: IndexPath) {
super.update(cell, at: indexPath)
cell.accessoryType = .disclosureIndicator
}
override func action(for cell: TableCell, at indexPath: IndexPath, sender: Any) {
switch dataSource.identifier(at: indexPath) {
case Id.cells:
show(CellsTVC(), sender: self)
case Id.form:
show(FormTVC(), sender: self)
case Id.settings:
show(MainSettingsTVC(), sender: self)
case Id.github:
show(GithubTVC(), sender: self)
default:
break
}
}
}
安装
-
.package(url: "https://github.com/tadija/AEViewModel.git", from: "0.9.2")
-
github "tadija/AEViewModel"
-
pod 'AEViewModel'
许可协议
此代码基于MIT协议发布。有关详细信息,请参阅LICENSE。