CacheTrackerConsumer
提供辅助类,用作CacheTracker和UI控件(UITableView、UICollectionView)之间的中介。它与缓存跟踪存储同步保持本地的单维或二维数组。UI控件可以直接与消费者交互。这对于使用VIPER架构非常有用。您将消费者放入VIEW,并通过PRESENTER将交易从INTERACTOR传递到您的VIEW。在这种情况下,VIEW保持被动,符合VIPER规则,其状态仅由PRESENTER控制。
更改
参见变更日志
类型
CacheTrackerPlainConsumer
仅在线性数组中保留普通项,与缓存跟踪存储同步,并为UI控件生成更新。
CacheTrackerPlainRecurrentConsumer
与CacheTrackerPlainConsumer功能类似,唯一的区别在于:当某个项被更新或移动到另一个索引时,可以将一些属性从旧值保存到新值,这对于某些应用场景很有用,例如,向数组添加新的“人”对象时,具有“个人编号”的该编号应保持不变,直到项从数组中删除,其他字段可以更改。
class Man: CacheTrackerPlainRecurrentConsumerItem {
let personalNumber: String // this value should be stay the same
let firstName: String // can change
let lastName: String // can change
init(personalNumber: String, firstName: String, lastName: String) {
self.personalNumber = personalNumber
self.firstName = firstName
self.lastName = lastName
}
// MARK: CacheTrackerPlainModel
init() {
personalNumber = ""
firstName = ""
lastName = ""
}
// MARK: CacheTrackerPlainRecurrentConsumerItem
func recurrentPlainConsumerItem(using oldValue: CacheTrackerPlainRecurrentConsumerItem) -> CacheTrackerPlainRecurrentConsumerItem {
let oldValue = oldValue as! Man
// Even if item is updated its 'personalNumber' will have the same value since it first time appeared in consumer.
return Man(personalNumber: oldValue.personalNumber, firstName: firstName, lastName: lastName)
}
}
let consumer = CacheTrackerPlainRecurrentConsumer<Man>()
consumer.willChange()
consumer.add(Man(personalNumber: "123", firstName:"John", lastName:"Smith"), at: 0)
consumer.didChange()
consumer.willChange()
// NOTE: new value "456" will be ignored
consumer.update(Man(personalNumber: "456", firstName:"John", lastName:"Smith"), at: 0)
consumer.didChange()
let personalNumber = consumer.object(at: 0).personalNumber
// will print '123', NOT '456'
print(personalNumber)
CacheTrackerSectionedConsumer
仅在分节数组中保留普通项,与缓存跟踪存储同步,并为UI控件生成更新。缓存跟踪生成线性数组中的普通项,但它们被转换为分节数组,UI控件似乎正在使用分节进行操作。可以说,“为什么不直接使用CacheTracker的分节通知?”- 答案是:我花费了两周时间分析CoreData/Realm FRC产生的通知行为,我理解,当在FRC中尝试使用分节时,这是一个“地狱”,而且在没有正常方法将项保存在自己的二维数组中以与仅由FRC管理的存储同步的情况下,所以我决定编写这些类以使其更容易。
注意:如果您有数百万条项目,则建议您直接与CacheTracker进行交互,因为性能问题。
使用方法
要与 CacheTrackerSectionedConsumer 一起使用,只需确保你的普通模型兼容 CacheTrackerSectionedConsumerModel 协议。完整的示例请参考 Demo 项目。
class PlainItem: CacheTrackerPlainModel, CacheTrackerSectionedConsumerModel {
let name: String
let section: String
init(name: String) {
self.name = name
self.section = String(name.prefix(1))
}
// MARK: - CacheTrackerPlainModel
required init() {
self.name = ""
self.section = ""
}
// MARK: - CacheTrackerSectionedConsumerModel
func sectionTitle() -> String {
return self.section
}
}
创建消费者并设置其代理
consumer = CacheTrackerSectionedConsumer<PlainItem>()
consumer.delegate = self
当使用 CacheTrackerSectionedConsumer 时,不要忘记将分区键作为第一个排序描述符传递(在这个例子中,我们希望使用 'section' 作为分区键)
let cacheRequest = CacheRequest(predicate: NSPredicate(value: true), sortDescriptors: [
NSSortDescriptor(key: #keyPath(CoreDataItem.section), ascending: true),
NSSortDescriptor(key: #keyPath(CoreDataItem.name), ascending: true)
])
如果需要,使用初始对象集填充消费者
- 使用动画插入
consumer.willChange()
consumer.consume(transactions: cacheTracker.transactionsForCurrentState())
consumer.didChange()
- 完全重新加载
consumer.reset(with: cacheTracker.transactionsForCurrentState())
tableView.reloadData()
注意:应在调用消费者的 consume()、add()、remove()、update() 的任何批量调用之前调用 willChange()。在调用所有批量操作后,必须像上面示例中那样完成与 didChange() 的交互。
实现表格视图数据源(或集合视图),在那里你可以调用消费者方法
- 当 cacheTrackerSectionOffset 为 0 且 cacheTrackerItemsOffset 为 0(默认)时
override func numberOfSections(in tableView: UITableView) -> Int {
return consumer.sectionsCount()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return consumer.numberOfItems(at: section)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return tableView.dequeueReusableCell(withIdentifier: "Default")!
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let item = consumer.object(at: indexPath)
cell.textLabel?.text = item.name
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let item = consumer.object(at: IndexPath(row: 0, section: section))
return item.section
}
- 当 cacheTrackerSectionOffset > 0 或 cacheTrackerItemsOffset > 0 时
override func numberOfSections(in tableView: UITableView) -> Int {
return self.globalSectionCount(1)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.isPlainDataSection(section) {
return consumer.numberOfItems() + cacheTrackerItemsOffset
}
else {
return 1
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if self.isPlainDataIndexPath(indexPath) {
return tableView.dequeueReusableCell(withIdentifier: "Default")!
}
else {
return tableView.dequeueReusableCell(withIdentifier: "Before")!
}
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if self.isPlainDataIndexPath(indexPath) {
let item = consumer.object(at: self.plainIndexPath(from: indexPath).row)
cell.textLabel?.text = item.name
}
else {
cell.textLabel?.text = "Before \(indexPath)"
}
}
偏移量
如果你想显示非起始位置的项目,则可以使用以下方式设置偏移量
- var cacheTrackerItemsOffset: Int = 0
- var cacheTrackerSectionOffset: Int = 0
它们控制数据分区/项目从表格视图或集合视图开始的偏移量。
更多详细示例请参考 Demo 项目
安装
Cocoapods
将以下内容添加到你的 Podfile 中
pod 'CacheTrackerConsumer'
如果您想使用UIKit扩展
pod 'CacheTrackerConsumer'
pod 'CacheTrackerConsumer/UIKit'
UIKit扩展包含了CacheTrackerPlainConsumerDelegate(或CacheTrackerSectionedConsumerDelegate)的默认实现,适用于UITableView、UICollectionView、UITableViewController、UICollectionViewController等...
最后,导入模块
import CacheTrackerConsumer