Tableau 0.1.0

Tableau 0.1.0

Aaron Bosnjak 维护。



Tableau 0.1.0

  • 作者:
  • Aaron Bosnjak

Tableau

Tableau 是一个库,用于使您的表格和集合视图设置更简洁、更声明式、更类型安全和更有趣。它包括对表格上自动比较和动画数据的支持,cell 可以发射自定义事件,并支持 RxSwift。

快速入门

使用 Tableau,您可以在每个部分的基础上“绑定”数据和信息、功能(例如数据、cell 类型或提供 cells 的闭包)、事件处理程序到您的表格(如果您的表格没有进行部分划分,则绑定到整个表格)。

对于进行部分划分的表格,我们首先声明一个 enum(或 struct)来定义您的部分,并创建一个“binder”对象

enum Section: TableViewSection {
    case first
    case second
    case third
    case fourth
}

let binder = SectionedTableViewBinder(tableView: self.tableView, sectionedBy: Section.self)

然后,我们开始“绑定链”到一个或多个部分,类似于 switch 语句中的情况。在这里,我们将创建三个“绑定链”以向我们刚刚声明的部分添加不同的数据和处理器

var myModels: [MyModel] = ...

binder.onSection(.first)
    .bind(headerTitle: "FIRST SECTION")
    .bind(cellType: MyCustomTableViewCell.self, models: { myModels })
    .onDequeue { (row: Int, cell: MyCustomTableViewCell, model: MyModel) in
        // setup the dequeued 'cell' with the 'model'
    }
    .onTapped { (row, cell, model: MyModel) in
        // e.g. go to a detail view controller with the 'model'
    }

binder.onSections(.second, .third)
    .bind(headerTitles: [.second: "SECOND SECTION", .third: "THIRD SECTION"])
    .bind(cellProvider: { (tableView, section, row, model: MyOtherModel) -> UITableViewCell in 
        if ... {
            return tableView.dequeue(MyOtherTableViewCell.self)
        } else {
            return tableView.dequeue(MyCustomTableViewCell.self)
        }
    }, models: { () -> [MyOtherModel]
        ...
    })
    .dimensions(
        .cellHeight { section, row, model in 
            return UITableViewAutomaticDimension 
        },
        .estimatedCellHeight { _, _ in 120 })
    ...
    
binder.onAllOtherSections()
    .bind(cellType: MyOtherOtherTableViewCell.self, viewModels: { ... })
    ...

cell 自动从给定的“model”数组中出队,并将绑定的模型和 cell 类型保留并传递给绑定链上的其他处理器。通过使用这种链式设置的表格,您可以更清楚地描述表格视图的工作方式,其阅读方式更类似于你收到的需求。

自定义 cell 事件

在组合视图(ViewController)中如何传回事件,如按钮点击或单元格文本输入事件,通常是个头疼的问题,通常需要遵守一堆不同的代理协议。为了解决这个问题,Tableau 也为你提供了能力,让单元格可以声明自定义事件枚举,这可以在你的绑定链中观察。这是通过遵守 ViewEventEmitting 协议并给它一个 ViewEvent 枚举来实现的,如下所示

class MyCustomTableViewCell: UITableViewCell, ViewEventEmitting {
    enum ViewEvent {
        case switchToggled(state: Bool)
        case buttonPressed
        case textEntered(text: String)
    }
    
    @objc func onSwitchToggled(switch: UISwitch) {
        self.emit(event: .switchToggled(state: switch.isOn)
    }
}

然后你可以观察单元格在绑定链中发射事件,如下所示

binder.onSection(.first)
    .bind(cellType: MyCustomTableViewCell.self, models: { myModels })
    .onEvent(from: MyCustomTableViewCell.self) { (row, cell, event, model: MyModel) in
        switch event {
        case .switchToggled(let state):
            // update something in the model
        case .buttonPressed:
            // perform some action
        case .textEntered(let text):
            // update something in the model
        }
    }

差分和动画

Tableau 支持 table 数据变化时的深度差分和自动动画。无需对模型进行特殊操作,Tableau 就能确定何时以及哪些部分的更新,并基于更改运行基本的计数,以在部分上应用 'reload'(重新加载)、'insert'(插入)和 'delete'(删除)动画。

然而,如果你需要的话,这个库也通过考虑在你的模型中遵守 EquatableCollectionIdentifiable 来提供更强大的、更细粒度的差分控制,提供更精确的 'reload'、'insert' 和 'delete' 动画,以及用于跟踪移动等,这一切都是自动完成的!

高级特性

虽然你是一个优秀的开发者,但当你审视这些示例时,你会考虑到可能遇到的局限性以及边缘情况(如每个部分有多个单元格或模型类型,自定义头部和尾部视图类型,部分数据在编译时不知晓,无限滚动等)。幸运的是,Tableau 支持更多高级特性,覆盖这些情况,并能够随着你表格的复杂性成比例扩展,因此你永远不会因为库的限制而不得不求助于常规 UIKit 的 'dataSource/delegate' 习惯。

Tableau 完全被文档化,包括在 Xcode 仓库中的 'TableauExample' 项目中的教程和代码示例(附有文档说明!)。以下是一些熟悉 Tableau 的教程

安装

Tableau(将会)通过 CocoaPods 提供。要安装它,只需将以下行添加到你的 Podfile 中

pod 'Tableau'

贡献者

Aaron Bosnjak(电子邮件:[email protected],Twitter: @aaron_bosnjak)

Tableau 欢迎贡献者!如果您有功能想法或错误修复,请随时提交拉取请求。问题和功能想法在以下Trello 任务板上进行跟踪。

鸣谢

Tableau 使用 Tony Arnold 编写的超级棒的 Differ 库分支来进行差异比较工作。如果您喜欢 Tableau,请确保也给该仓库点个 star!

许可证

Tableau 在 MIT 许可证下发布,您可以随意使用它。请参阅 LICENSE 文件以获取更多信息。