DiffTableDirector 1.0.4

DiffTableDirector 1.0.4

Lavrinenko Aleksandr 维护。



 
依赖关系
SwiftLint>= 0
DeepDiff= 2.3.1
 

  • aleksiosdev

DiffTableDirector

CI Status Version License Platform

DiffTableDirector - 在 UITableViewDataSource & Delegate 之上的轻量级抽象。它允许您以声明式、类型安全的方式与表格一起工作。提供许多在原始表格视图中缺少的实用功能。

底层库使用 iOS 13 的原生 Diff API 和 iOS 11 & 12 的 DeppDiff。

特点

  • 类型安全的泛型单元格、头部和尾部
  • 自动注册表格元素
  • 通过代理支持单元格/头部/尾部的动作
  • 空的表格视图的可替换视图
  • 观察表格边界交叉
  • 使用您的加载器和动画轻松分页
  • 在主线程或后台队列上通过 diff 更新表格
  • 易于扩展

示例

要运行示例项目,请先克隆仓库,然后在 Example 目录中运行 pod install

用法

创建 TableDirector

import DiffTableKit

let tableDirector = TableDirector(tableView: awesomeTableView)
    
// or

let tableDirector = TableDirector()
// Later 
tableDirector.connect(to: awesomeTableView)

支持单元格的可配置协议

import DiffTableKit

// MARK: - ConfigurableCell
extension SuperCell: ConfigurableCell  {
	struct ViewModel: Hashable {
    let ID: String 
		let name: String
		let icon: UIImage
	}

	func configure(_ item: ViewModel) {
		_nameLabel.text = item.name
		_iconImageView.set(image: item.icon)
	}
}

// MARK: - ConfigurableHeaderFooter
extension SuperHeader: ConfigurableHeaderFooter {
	typealias ViewModel = String

	func configure(_ item: ViewModel) {
		_titleLabel.text = item
	}
}

填充您的表格

import DiffTableKit

let row = TableRow<SuperCell>(item: .init(ID: "uniqIdentifier", title: "Ttile", icon: UIImage(named: "icon"))
let header = TableHeader(item: "Header title")
let section = TableSection(rows: [row], headerConfigurator: header)
tableDirector.reload(with: [section])

你就是最好的。代码工作正常,表格已填充。简单,安全,所有项目均自动注册。但等等……如果按下其他单元格会发生什么?

操作

由代表执行的操作。你也可以使用回调达到相同的效果

extension SuperCell: ActionCell {
	typealias ViewModel = InfoViewModel
	typealias Delegate = CellPressableDelegate

	func configure(_ item: InfoViewModel) {
		// Fill your cell with data here
		_titleLabel.text = item.title
		
		contentView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didPressedCell)))
	}

	@objc func didPressedCell() {
		delegate?.didPressedCell(self)
	}
	
}

创建单元格也将有些变化

let actionRow = TableActionRow<SuperCell>(viewModel: .init(ID: "uniqIdentifier", title: "Ttile", icon: UIImage(named: "icon"), delegate: self)
tableDirector.reload(with: [actionRow])

同样对于表头)

占位符视图

有时表格为空,看起来很丑。解决方案之一是为空状态添加漂亮的图片。让我们来实现它

import DiffTableKit
tableDirector.addEmptyStateView(viewFactory: { [unowned self] in
			// Called on main thread
			return PlaceholderView()
			}, position: .center)

现在你可以看到当表格为空时占位符视图居中显示。当加载一些内容时,它就会消失。

差异

表格可以显示很多不同的信息。它可以单独提供。用动画方式更新表格,而不是多次重新加载。苹果已经提供了UIDiffableDataSource,但只适用于iOS 13。我们从它中得到灵感,以声明式方法提供相同解决方案。适用于iOS 11+)

所以我们需要做两个步骤

步骤1:为所有你的ViewModel实现Hashable协议。最好提供一些独一无二的ID,比如UUID().uuidString。

struct ViewModel: Hashable {
      let id: String
	... 
}

TableDirector会在模型不可哈希时进行差异比较。但是同一个模型对于它来说是不同的。请记住这一点,并最好使用非可哈希的ViewModel来制作不可变的表格。

iOS 13中每次重新加载都将可比较。如果你提供了动画,你会看到动画。对于false情况,它将看起来像老式的reloadData。

你也可以手动触发可比较的重新加载(例如对于较低的iOS)。在主队列和你的队列上。

	func reload(with sections: sections, reloadRule: .calculateSync) // Will calculate diff on main thread. 

	func reload(with sections: sections, reloadRule: .calculateAsync(queue: yourQueue) // Will calculate on queue your provider. Can prevent freeze for big collections
	

请注意:苹果建议仅在主队列或仅在后台队列上重新加载表格。错过队列可能导致未定义的行为。

跨界限

记得那个当你需要为滚动时的表DRAWmouseover阴影在上面的视图?还是也许 separator? 在滚动状态中隐藏它。

_tableDirector?.topCrossObserver = CrossObserver(didCross: {
	// Draw your shadow/separator here
}, didReturn: {
	// Hide shadow/separator here
})

底部边框也可以做到同样的效果。我们在其他项目中使用了它,而且真的很简单

分页和下拉刷新

顺便说一句。这种分页方式看起来不错!分页在业务逻辑中总是个头疼的问题。但如果你想要一些自定义行为或动画——UI也可能很棘手。因此我们提供了一个组件。它可以进行配置。可以控制视图的动画和生命周期。就像这样

let loader = Loader(view: viewForPagination, animator: yourAnimator)
let bottomPaginationController = PaginationController(
	settings: .bottom,
	loader: yourLoader) { (handler) in
		network.request(...) {
			// Change state of pagination. Also your can call same method on pagination controller
			handler.finished(isSuccessfull: true, canLoadNext: true)
		}
	}
self._tableDirector?.add(paginationController: bottomPaginationController)

加载器是简单的结构

public struct Loader {
	let view: UIView
	let animator: PaginationControllerLoaderAnimator
}

为您的加载器提供动画的类应该符合 PaginationControllerLoaderAnimator。

public protocol PaginationControllerLoaderAnimator {
	/// Animate loader base on state
	/// - Parameter state: loader state
	func animate(state: PaginationController.Loader.State)
}

/// Loader states
public enum State {
	case initial
	case loading
	case error
	case success
}

足以处理大多数情况。同样的方法也适用于下拉刷新。只需为 PaginationController 提供向上方向即可

要求

iOS 11.0+ Xcode 11.0+ Swift 5.0

安装

DiffTableDirector 通过 CocoaPods 提供。要安装,只需将以下行添加到您的 Podfile 中

pod 'DiffTableDirector'

作者

aleksiosdev, [email protected]

许可证

DiffTableDirector 在 Apache-2.0 许可证下提供。有关更多信息,请参阅 许可证文件