AEViewModel 0.9.2

AEViewModel 0.9.2

tadija 维护。



Swift 5.1 Platforms iOS CocoaPods Carthage SPM License MIT

AEViewModel

Swift 实用工具,方便创建表格和集合视图

我制作这个是为了个人使用,但欢迎使用或贡献。更多示例请查看 源代码测试

AEViewModel

索引

简介

几乎所有应用程序的界面都常常在表格或集合视图中。这是我为自身简化这项任务的一些尝试,希望能为其他人提供帮助。

此解决方案背后的理念相当抽象,这意味着它可以以许多不同(或风格)的方式应用于多个场合或喜好。

通过适当使用此框架,它将强制您通过利用 MVVM 模式的概念来编写更干净和可维护的代码。为了更好地理解它,您首先应该熟悉 “手动”方法

它可能不是快速和容易(对于每个人)就能掌握的,但如果你给它机会,你可能永远不会想在不使用它的情况下创建另一个表格或集合视图...

功能

  • 更快地创建自定义表格/集合界面,减少样板代码
  • 适用于静态(菜单、表单等)和动态(本地、远程等)数据
  • 提供常用单元格(文本输入、滑块、开关、按钮等)

使用方法

数据源

建议首先熟悉 DataSource.swift,因为你会用这些功能做几乎所有的事情。

这些都是从 DataSource 开始的非常简单的协议,必须具有部分,然后每个 Section 必须包含项目,其中每个 Item 包含 identifier: StringviewModel: ViewModel 和可选的 child: DataSource?

/// ViewModel is whatever you choose it to be, easy like this:
struct MyCustomWhatever: ViewModel {}

基本数据源

BasicDataSource.swift 中有符合所有这些协议的简单结构,大多数情况下可以使用这些结构。由于这些结构也符合 Codable,还可以从 JSON 数据等创建 BasicDataSource

对于更具体的功能,创建符合这些协议的自定义类型,并使用这些类型替代。

单元格

Cell.swift 中有一个简单的协议,用于表格和集合视图单元格。请注意,TableCellCollectionCell 仅是简单的类型别名

public typealias TableCell = UITableViewCell & Cell
public typealias CollectionCell = UICollectionViewCell & Cell

创建自定义单元格时,最简单的方法是从 TableCellBasicCollectionCellBasic 继承,并覆盖该协议的这些方法

/// 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
        }
    }
    
}

安装

许可协议

此代码基于MIT协议发布。有关详细信息,请参阅LICENSE