T21TableViewDataSource 2.1.0

T21TableViewDataSource 2.1.0

Salvador MartinMarcos Molero 维护。



  • Eloi Guzman Ceron

T21TableViewDataSource

管理 UITableView 数据操作的辅助类。

Carthage compatible CocoaPods compatible Swift compatible Platform compatible License SwiftPM

TableViewDataSource 类是一个辅助类,用于管理表格数据操作,如 添加、删除更新。它提供了一个简单的方法来更新表格数据源,应用具体的排序 并避免在数据源中添加已经存在的实体时的项目重复。

这些功能主要包含以下两个特点

  • 动画支持,这对表格视图来说相当复杂。
  • 所有昂贵的计算都在后台队列中完成,以避免影响主线程。

以下 gif 展示了一个示例,随机添加、删除和更新项目。由于我们不再使用 reloadData 方法,因此可以在添加/删除/更新项目时滚动表格视图,并且滚动也会得到保留,无需任何奇怪的技巧。这是我的 懒加载 表格视图的绝佳解决方案 :D!

示例 1 示例 2

安装

T21TableViewDataSource 通过 CarthageCocoaPodsSwift 包管理器 提供。

Carthage

要使用 Carthage 安装 T21TableViewDataSource,请将以下行添加到您的 Cartfile

github "worldline-spain/T21TableViewDataSource"

然后运行carthage update --no-use-binaries命令或直接运行carthage update。有关Carthage的安装和使用详情,请访问其项目页面

CocoaPods

要使用CocoaPods安装T21TableViewDataSource,请将以下行添加到您的Podfile中。

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0' 
use_frameworks!

pod 'T21TableViewDataSource'

然后运行pod install命令。有关CocoaPods的安装和使用详情,请访问其官方网站

Swift Package Manager

要使用Apple的Swift Package Manager集成,请将以下内容添加到您的Package.swift中作为依赖项。

.package(url: "https://github.com/worldline-spain/T21TableViewDataSource.git", .upToNextMajor(from: "2.0.0"))

有关Swift Package Manager的安装和使用详情,请访问其官方网站

如何使用

重要:目前,此版本仅支持单节段UITableView。我们希望在未来支持多节段。

设置一个数据源并配置其TableView

为了创建一个新的数据源类,这可以理解为一个简单的项目数组,很简单。

class ViewController: UIViewController {
    let dataSource = TableViewDataSource<DataSourceItem>()

    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        dataSource.tableView = tableView
    }
}

TableViewDataSource是一个模板类,在这种情况下,在构造函数中使用DataSourceItem类型指定内部数组的ItemType,在这种情况下,它们将是DataSourceItem实例。

TableViewDataSource<DataSourceItem>()

稍后我们将看到什么是DataSourceItem类,目前让我们假设它是一个数据容器类,它持有显示单元格所需的数据。例如,我们可以使用简单的IntString类。

[DataSourceItem,DataSourceItem,DataSourceItem] or [1,2,3,4,5] or ["a","b","c","d","e"]

当我们将tableView分配给我们的dataSource实例时,这个实例就会将tableview委托和数据源处理程序设置为dataSource本身。dataSource类实现了UITableViewDataSourceUITableViewDelegate协议。

以下块可以轻松配置与UITableView协议相关的方法

public var onTableViewDidSetFunction: (_ tableView: UITableView?) -> Void

public var cellForRowFunction: (_ tableView: UITableView, _ indexPath: IndexPath, _ item: ItemType) -> (UITableViewCell)

public var heightForRowFunction: (_ tableView: UITableView,_ indexPath: IndexPath, _ item: ItemType) -> CGFloat

public var didSelectRowFunction: (_ tableView: UITableView,_ indexPath: IndexPath, _ item: ItemType) -> Void

public var didDeselectRowFunction: (_ tableView: UITableView,_ indexPath: IndexPath, _ item: ItemType) -> Void

public var willSelectRowFunction: (_ tableView: UITableView,_ indexPath: IndexPath, _ item: ItemType) -> IndexPath?

public var willDeselectRowFunction: (_ tableView: UITableView,_ indexPath: IndexPath, _ item: ItemType) -> IndexPath?

这些块接受以下参数

  • 避免不必要地保留相关tableView。
  • 相关行索引路径。
  • 该行的 ItemType 实例(在这种情况下将是 DataSourceItem)。

当 tableView 被设置为 DataSource 时,将执行 onTableViewDidSetFunction 块。当注册类或 Nib 文件作为单元格标识符时,该功能非常有用。

使用 blocks 配置 DataSource 提供了重用更多代码的可能性,同时也提供了使用工厂方法创建类似 DataSource 对象的可能性。

如果 blocks 无法达到期望的行为,并不需要 subclass DataSource 类来添加更多协议方法应该不是问题。

配置cellForRow块

以下代码演示了如何添加一个非常简单的cellForRow块。

dataSource.cellForRowFunction = { (tableview, indexpath, item) in
    var cell = tableview.dequeueReusableCell(withIdentifier: "cell")
    
    if cell == nil {
        cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "cell")
    }

    let title = item.value as! String
    cell?.textLabel?.text = title
    
    return cell!
}

在这种情况下,将DataSourceItem值转换成String来设置标题。

ItemType泛型类型

TableViewDataSource类使用了一个泛型的ItemType来作为内部项。这个ItemType 必须 实现以下 协议: DataSourceComparableHashable

open class TableViewDataSource<ItemType: Any> : NSObject, UITableViewDataSource, UITableViewDelegate where ItemType: DataSourceComparable, ItemType: Hashable {
....
}

public protocol DataSourceComparable {
    static func <(lhs: Self, rhs: Self) -> Bool
}

public protocol Hashable : Equatable {
    /// The hash value.
    ///
    /// Hash values are not guaranteed to be equal across different executions of
    /// your program. Do not save hash values to use during a future execution.
    public var hashValue: Int { get }
}
  • Hashable 是 Swift 库中的一个标准协议,它将在内部用于检查一个项目是否已经存在于 DataSource 集合中,或者是否是新的项目。
  • DataSourceComparable 是一个自定义协议,它基于 小于运算符 构建。它将用于应用行排序。

添加到 DataSource 的每个项目都必须符合这些协议。客户端可以创建自己的类型实现协议,或者也可以使用现有的容器类 DataSourceItem。

DataSourceItem类

DataSourceItem类只是一个简单的包装类,它已经实现了所需协议 HashableDataSourceComparable

public class DataSourceItem : DataSourceComparable, Hashable {

public private(set) var value: Any

public private(set) var uid: String

public private(set) var index: Float

public init(_ value: Any, _ uid: String, _ index: Float = default)
}

其主要目的是为了提供使用 Any 值将不同类型的项添加到 DataSource 的可能性。当然,我们随后将不得不使用 downcast as! 来访问不同的类型。例如,我们可以有

// if we want to add this kind of items to the DataSource they should be subclasses of Animal class: 
let items: [Animal] = [Lion(),Elephant(),Zebra()]

// with the DataSourceItem we can have completely different ItemTypes.
let items: [DataSourceItem] =  [DataSourceItem(Lion),DataSourceItem(Train),DataSourceItem(Elephant),DataSourceItem(Plane),DataSourceItem(Zebra),DataSourceItem(Car)]
  • DataSourceItem提供了一个 uid,它用作项目的 唯一标识符
  • 还有一个 Float 索引 值,用于应用具体的 排序

客户端始终可以创建自己的类,但在大多数情况下,DataSourceItem都能满足TableView DataSource的需求。

应用一个具体的排序函数

默认情况下,DataSource应用以下升序排序函数

public var sortingFunction: ( _ a: ItemType, _ b: ItemType) -> Bool = { return $0 < $1 }

在这种情况下,由于ItemTypes实现了DataSourceComparable,它们可以很容易地比较。客户端可以设置更复杂的排序函数。

通过其唯一ID更新现有项目

TableViewDataSource类的一个特性是可以通过其唯一标识符更新现有行/项目。在这个例子中,我们添加了3个项目(在本例中,我们的类型将是简单的字符串),然后我们更新了第一个添加的项目,并为其分配了新的标题和新的排序值。

let itemA = DataSourceItem("This item is A","itemA",1.0)
let itemB = DataSourceItem("This item is B","itemB",2.0)
let itemC = DataSourceItem("This item is C","itemC",3.0)

self.dataSource.addItems([itemB,itemA,itemC]) // we are adding our items unsorted (B,A,C)

// The resulting table view is sorted automatically to (A,B,C):
// - This item is A
// - This item is B
// - This item is C

现在让我们将itemC的标题更新为“Updated title for C”。

let newItemC = DataSourceItem("Updated title for C","itemC",3.0)
self.dataSource.addItems([newItemC])

// The resulting table view is updated automatically:
// - This item is A
// - This item is B
// - Updated title for C

现在让我们将itemC的索引(排序值)更新为0.5。

let newItemC = DataSourceItem("Updated title for C","itemC",0.5)
self.dataSource.addItems([newItemC])

// The resulting table view is sorted automatically:
// - Updated title for C
// - This item is A
// - This item is B

DataSource通过检查具有同一标识符“itemC”的项目来检查该项目是否已经存在并更新其值。请记住,客户端可以创建自定义类来应用不同的排序,比如使用`Date`、`String`或其他任何他想要的方式。

向DataSource类添加项目

在上面的例子中,我们已经看到如何向DataSource添加项目,只要记住,添加项目可能涉及到

  • 对于现有项目,将进行更新(单元格可能会重新加载,或者如果排序发生变化,则创建新的单元格)
  • 对于新项目,将进行添加(创建新的单元格)

从DataSource类中删除项目

删除项目与添加项目一样简单。

dataSource.removeItems([DataSourceItem("","itemC")])

DataSource accessors

客户端可以询问数据源实例它们有多少项。

let count = dataSource.count

它们还可以使用下标运算符来检索内部项。

let item4 = dataSource[4]

修改内部项的唯一方法是使用指定的方法。

作者

  • Eloi Guzman Ceron - 初始工作
  • Edwin Peña - 初始工作
  • Salvador Martin - 初始工作
  • Patricia De la Rica - Carthage集成
  • Marcos Molero - Swift 5集成和Carthage集成

有关参与该项目的贡献者列表,请参阅贡献者列表

许可

本项目采用MIT许可协议 - 有关详细信息,请参阅LICENSE.md文件。

致谢

  • 献给Worldline Spain iOS开发团队。