CascadingTableDelegate 4.0.0

CascadingTableDelegate 4.0.0

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布最后发布2019年10月
SPM支持 SPM

Ricardo Pramana Suranta 维护。



CascadingTableDelegate

CI Status Swift 5.0 Platform

Version Carthage compatible License

以简洁的方式编写更清洁的 UITableViewDelegateUITableViewDataSource

为什么创建这个库?

在常见的 iOS 开发中,UITableView 已经成为用于构建具有重复元素丰富页面的主流。例如,这个页面

Sample Page

(感谢 Wieky 帮助我设计这个示例页面!)😁)

然而,使用 UITableView 也有其问题。

正如您所知,为了显示内容,UITableView 使用与 UITableViewDelegateUITableViewDataSource 兼容的对象。由于 UITableView 只允许一个对象成为 delegatedataSource,这往往是让我头疼的原因。这些限制可能导致代码文件变得非常大——由于“巨眼方法”所致。这个问题的常见受害者包括 tableView(_:cellForRowAt:)tableView(_:heightForRowAt:)tableView(_:didSelectRowAt:)

正因为如此,有时我会想,如果可以的话,将 delegatedataSource 方法调用 拆分到每个部分或行 会很方便。

了解CascadingTableDelegate。

CascadingTableDelegate 是一种将 UITableViewDelegateUITableViewDataSource 分解成树形结构的方法,灵感来源于 组合模式。以下是协议的简化结构(文档较少)

public protocol CascadingTableDelegate: UITableViewDataSource, UITableViewDelegate {

	/// Index of this instance in its parent's `childDelegates`. Will be set by the parent.
	var index: Int { get set }

	/// Array of child `CascadingTableDelegate` instances.
	var childDelegates: [CascadingTableDelegate] { get set }

	/// Weak reference to this instance's parent `CascadingTableDelegate`.
	weak var parentDelegate: CascadingTableDelegate? { get set }

	/**
	Base initializer for this instance.

	- parameter index:          `index` value for this instance. May be changed later, including this instance's `parentDelegate`.
	- parameter childDelegates: Array of child `CascadingTableDelegate`s.

	- returns: This class' instance.
	*/
	init(index: Int, childDelegates: [CascadingTableDelegate])

	/**
	Preparation method that will be called by this instance's parent, normally in the first time.

	- note: This method could be used for a wide range of purposes, e.g. registering table view cells.
	- note: If this called manually, it should call this instance child's `prepare(tableView:)` method.

	- parameter tableView: `UITableView` instance.
	*/
	func prepare(tableView tableView: UITableView)
}

简而言之,此协议允许我们将接收到的任何 UITableViewDelegateUITableViewDataSource 方法的调用根据传递的 IndexPathsectionrow 值传递给其子级。

但是,UITableViewDelegate 和 UITableViewDataSource 有很多方法!谁来传播所有这些调用呢?

不必担心,这个库通过创建两个可立即使用的类来完成这项繁重的工作,即 两个类CascadingRootTableDelegateCascadingSectionTableDelegate。这两个类都实现了 CascadingTableDelegate 协议和传播逻辑,但有不同的用途

  • CascadingRootTableDelegate:

    • 充当 UITableView 的主要 UITableViewDelegateUITableViewDataSource
    • 根据传递的 IndexPathsection 值和子级的 index,将几乎所有的代理和数据源调用传递到其 childDelegates 上。
    • 对于 numberOfSections(in:) 调用,返回它的 childDelegates 的数量。
  • CascadingSectionTableDelegate:

    • 不会将其自身设置为传递的 UITableViewUITableViewDelegateUITableViewDataSource,而是等待其 parentDelegate 方法的调用。
    • 就像 CascadingRootTableDelegate 一样,它也将几乎所有代理和数据源调用传播到其 childDelegates 上,但根据传递的 IndexPathrow
    • 对于 tableView(_:numberOfRowsInSection:) 调用,返回它的 childDelegates 的数量。

这是一张图,说明了 tableView(_:cellForRowAt:) 调用如何作用到这些类上

Example Logic Diagram

这两个类也接受您自定义的 CascadingTableDelegate 实现作为它们的 childDelegates(实际上只有一个用来添加一些新属性和方法的 UITableViewDataSourceUITableViewDelegate)。此外,您可以对该中的任何一个进行子类化,并调用重写方法中的 super 以让它们执行传播 - 类似于 责任链 的风格😉

以下是上面长页如何分割成段落在示例代码中的片段

Section Delegates

然后,将所有部分代理类添加到单个 CascadingRootTableDelegate 中。其 childDelegates 的序列或组成中的任何更改都将影响显示的表。克隆此存储库,并在示例项目中尝试它!😁

优点与缺点

优点

在使用CascadingTableDelegate后,我们可以

  • UITableViewDataSourceUITableViewDelegate的方法细化到每个分区或行,从而得到更清洁、更隔离的代码。
  • 使用熟悉的UITableViewDataSourceUITableViewDelegate方法,这使代码迁移更加容易。

其他优点

  • 所有实现的方法CascadingRootTableDelegateCascadingSectionTableDelegate上都经过单元测试!要运行测试,您可以
    • 打开示例项目并运行可用的测试,或
    • 在终端中执行run_tests.sh
  • 本库可通过Cocoapods和Carthage获得!😉

缺点

1. 未传播的特殊方法

正如您所知,并非所有的UITableViewDelegate方法都使用单个IndexPath作为它们的参数,这使得调用传播不太直观。基于这个原因,CascadingRootTableDelegateCascadingSectionTableDelegate没有实现这些UITableViewDelegate方法

  • sectionIndexTitles(for:)
  • tableView(_:sectionForSectionIndexTitle:at:)
  • tableView(_:moveRowAt:to:)
  • tableView(_:shouldUpdateFocusIn:)
  • tableView(_:didUpdateFocusInContext:with:)
  • indexPathForPreferredFocusedView(in:)
  • tableView(_:targetIndexPathForMoveFromRowAt: toProposedIndexPath:)

如果您需要实现其中任何一种,请自由继承这两种方法并添加您自己的实现!😁

2. tableView(_:estimatedHeightFor...方法处理

有三个可选的UITableViewDelegate方法用于估计高度

  • tableView(_:estimatedHeightForRowAt:),
  • tableView(_:estimatedHeightForHeaderInSection:),以及
  • tableView(_:estimatedHeightForFooterInSection:).

CascadingRootTableDelegateCascadingSectionTableDelegate 实现了这些调用,以便将其传播到 childDelegates。由于它们都实现了这些方法,因此将允许 UITableView 一直调用这些方法,针对每一个 childDelegates,如果它们发现了任何实现这些方法的子项。

为了防止布局损坏,CascadingRootTableDelegateCascadingSectionTableDelegate 将调用子代理的 tableView(_:heightFor...:) 对应方法来占位不实现的方法,这样 UITableView 将正确地渲染它。如果你的 tableView(_:heightFor...:) 方法使用了大量的计算,建议实现它们的 tableView(_:estimatedHeightFor...:) 对应方法。

如果两个方法都没有被 childDelegate 实现,CascadingRootTableDelegateCascadingSectionTableDelegate 将为 tableView(_:estimatedHeightForRowAt:) 返回 UITableViewAutomaticDimension,而为 tableView(_:estimatedHeightForHeaderInSection:)tableView(_:estimatedHeightForFooterInSection:) 返回 0

有关每个方法默认返回值(如有)的详细信息,请参阅 默认返回值文档

3. 为 parentDelegate 添加 weak 声明

Swift 不允许我们在协议中使用 weak 修饰符,但我们需要在 CascadingTableDelegateparentDelegate 属性中。请手动在您符合 CascasdingTableDelegate 的类中 parentDelegate 属性的前面添加 weak 修饰符,以防止引用循环!😁

仍然,如果您认为手动输入是一项繁琐的工作,则只需从 CascadingBareTableDelegate 继承。它是对 CascadingTableDelegate 的裸实现,不包含传播逻辑。🙂

示例

要运行示例项目,请先克隆存储库,然后首先从示例目录运行 pod install

需求

以下是版本列表及其对应的 Swift 版本

Swift 版本 CascadingTableDelegate 版本
5.0 4.x
4.2 3.2.x
4.0 3.0.x
3.x 2.x
2.2 1.x

安装

Cocoapods

要使用 CocoaPods 安装 CascadingTableDelegate,只需在 Podfile 中添加以下行:

pod "CascadingTableDelegate", "~> 3.2"

Carthage

要使用 Carthage 安装 CascadingTableDelegate,只需在 Cartfile 中添加以下行:

github "edopelawi/CascadingTableDelegate" ~> 3.0

作者

By Ricardo Pramana Suranta,[email protected]

许可

CascadingTableDelegate 在 MIT 许可下可用。有关更多信息,请参阅 LICENSE 文件。