T21TableViewDataSource
管理 UITableView 数据操作的辅助类。
TableViewDataSource 类是一个辅助类,用于管理表格数据操作,如 添加、删除 和 更新。它提供了一个简单的方法来更新表格数据源,应用具体的排序 并避免在数据源中添加已经存在的实体时的项目重复。
这些功能主要包含以下两个特点
- 动画支持,这对表格视图来说相当复杂。
- 所有昂贵的计算都在后台队列中完成,以避免影响主线程。
以下 gif 展示了一个示例,随机添加、删除和更新项目。由于我们不再使用 reloadData
方法,因此可以在添加/删除/更新项目时滚动表格视图,并且滚动也会得到保留,无需任何奇怪的技巧。这是我的 懒加载 表格视图的绝佳解决方案 :D!
示例 1 | 示例 2 |
---|---|
![]() |
![]() |
安装
T21TableViewDataSource 通过 Carthage、CocoaPods 或 Swift 包管理器 提供。
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
类,目前让我们假设它是一个数据容器类,它持有显示单元格所需的数据。例如,我们可以使用简单的Int或String类。
[DataSourceItem,DataSourceItem,DataSourceItem] or [1,2,3,4,5] or ["a","b","c","d","e"]
当我们将tableView分配给我们的dataSource实例时,这个实例就会将tableview委托和数据源处理程序设置为dataSource本身。dataSource类实现了UITableViewDataSource
和UITableViewDelegate
协议。
以下块可以轻松配置与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
必须 实现以下 协议: DataSourceComparable
和 Hashable
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类只是一个简单的包装类,它已经实现了所需协议 Hashable 和 DataSourceComparable。
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开发团队。