MBDataSource 5.1.0

MBDataSource 5.1.0

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布最后发布2017 年 11 月
SwiftSwift 版本3.0
SPM支持 SPM

Maintained by Matthias Buchetics.




DataSource

Swift 4
Carthage compatible
CocoaPods compatible

一个框架,用于简化 UITableView 数据源和单元 Cells 的设置和配置。它允许对 UITableViewDataSource 和(可选)UITableViewDelegate 进行安全的类型设置。《DataSource》还提供开箱即用的 diff 和动画删除、插入、移动和更改。

使用方法

示例应用包括展示 DataSource 功能的示例。示例展示了各种使用案例,从简单字符串列表到更复杂的用例,例如动态表单的设置。

入门指南

创建一个使用数据模型(Example)进行配置的 CellDescriptor(在此情况下,为 TitleCell)的 DataSource
此外,我们还添加了一个处理 didSelect 的事件处理器,该处理器处理 UITableViewDelegatedidSelectRowAtIndexPath 方法。

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 设置为 UITableViewdataSourcedelegate

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(...))。也支持诸如 heightForHeaderInSectionheaderHeight)之类的委托方法。

Diffing

如果你的数据模型实现了 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
    }
}

请参考示例代码的全文。

隐藏行和章节

两者,CellDescriptorSectionDescriptor 都提供了一个 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 方法,提供了一个方便的接口。在大多数情况下,你可以在 CellDescriptorSectionDescriptor 上定义这些闭包。然而,有时这会导致代码重复,例如,如果你有不同的单元格,但针对选择的代码执行是相同的。在这种情况下,你可以在 DataSource 本身设置委托闭包。

dataSource.didSelect = { (row, indexPath) in
    print("selected")
    return .deselect
}

如果 CellDescriptor(或 SectionDescriptor)上没有定义特定委托方法的闭包,则这些闭包将用作回退。

此外,您还可以设置回退的 UITableViewDelegateUITableViewDataSource,在 CellDescriptorSectionDescriptor 上未设置对应闭包时使用。

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

安装

Carthage

将以下行添加到您的 Cartfile

github "mbuchetics/DataSource", ~> 5.0

然后运行 carthage update

CocoaPods

对于 DataSource,使用以下 Podfile 中的条目

pod 'MBDataSource'

然后运行 pod install

在任何您想要使用 DataSource 的文件中,别忘了使用 import DataSource 导入框架。

手动

只需将 DataSource 文件夹中的 .swift 文件拖放到您的项目中即可。

贡献

  • 创建一些很酷的东西,让代码变得更好,增加一些功能等(这是最难的)。
    分支
  • 把它Fork到你的GitHub账户上。
  • 创建一个新的分支来进行更改。
  • 将您的所有更改提交到您的分支。
  • 提交一个 pull request

联系方式

您可以通过 matthias.buchetics.com 或在 Twitter 上关注我与我联系。