Composed 是一个面向协议的框架,用于组合应用中来自各种来源的数据。它为常见的用例提供了各种具体的实现。

如果您更喜欢查看代码,这里有一个演示项目:ComposedDemo

该库将所有内容都基于两个基本元素:SectionSectionProvider

使用 Composed 的主要好处包括:

  1. 入门
  2. 幕后原理
  3. 用户界面

入门

Composed 包含 3 个预定义的 section 以及 2 个 provider,应该可以满足大多数应用程序的需求。

Sections

ArraySection 表示通过 Array 管理其元素的 section。这种类型的 section 适用于表示内存中的数据。

ManagedSection 表示通过 NSFetchedResultsController 提供其元素的 section。此 section 适用于表示由 CoreData 管理的数据。

SingleElementSection 表示管理单个元素的 section。此 section 适用于只有单个元素要管理的情况。提示:使用 Optional<T> 来表示可能存在也可能不存在的元素。

Providers

ComposedSectionProvider 表示 SectionSectionProvider 的集合。该 provider 支持无限嵌套,包括其他 ComposedSectionProvider。所有子项将始终处于活动状态,因此 numberOfSectionsnumberOfElements(in:) 将返回代表所有子项的值。

SegmentedSectionProvider 表示 SectionSectionProvider 的集合。该 provider 支持无限嵌套,包括其他 SegmentedSectionProvider。任何时候都只有一个或零个子项可能处于活动状态,因此 numberOfSectionsnumberOfElements(in:) 将仅返回代表当前活动子项的值。

示例

假设我们想要表示用户的联系人库。我们的联系人将有 2 个组:家人和朋友。使用 Composed,我们可以轻松地将其建模如下:

let family = ArraySection<Person>()
family.append(Person(name: "Dad"))
family.append(Person(name: "Mum"))

let friends = ArraySection<Person>()
friends.append(Person(name: "Best mate"))

此时,我们有两个单独的 section 用于表示我们的两组联系人。现在我们可以使用一个 provider 将这两个 section 组合在一起:

let contacts = ComposedSectionProvider()
contacts.append(family)
contacts.append(friends)

就是这样!现在我们可以使用 provider 查询我们的数据,而无需任何一个单独的 section 知道它们现在包含在更大的结构中。

contacts.numberOfSections        // 2
contacts.numberOfElements(in: 1) // 1

如果我们想查询 section 中的单个数据(假设我们还没有对它的引用):

let people = contacts.sections[0] as? ArraySection<Person>
people.element(at: 1)            // Mum

注意:我们必须将 section 转换为已知类型,因为 SectionProvider 可以包含任何类型的 section 以及其他嵌套的 provider。

选择加入行为

如果我们现在继承 ArraySection,我们可以通过协议一致性来扩展我们的 section,以做一些更有趣的事情:

final class People: ArraySection<Person> { ... }

protocol SelectionHandling: Section { 
    func didSelect(at index: Int)
}

extension People: SelectionHandling {
	func didSelect(at index: Int) {
		let person = element(at: index)
		print(person.name)
	}
}

为了使这个工作正常进行,某些东西需要调用 didSelect,因此为了本示例的目的,我们将省略一些细节,但让您预览一下如何自己构建类似的东西:

// Assume we want to select the 2nd element in the 1st section
let section = provider.sections[0] as? SelectionHandling
section?.didSelect(at: 1)        // Mum

Composed 正在处理所有的映射和结构,允许我们完全专注于行为和扩展。

幕后原理

Section

Section 正如其名称所示,代表单个 section。最好的事情是,我们在一个 section 中不需要 IndexPath,只需要索引!

SectionProvider

Section provider 是一种容器类型,包含 section 或其他 provider。允许无限嵌套,因此具有无限的可能性。

Mappings(映射)

Mappings 提供了你的“树”结构和最终的 flattened 结构之间的粘合剂。让我们看一个例子。

// we can define our structure as such:
- Provider
    - Section 1
    - Provider
        - Section 2
        - Section 3
    - Section 4

// mappings will then convert this to:
- Section 1
- Section 2
- Section 3
- Section 4

此外,mappings 负责从本地索引到全局索引的转换,更重要的是负责 IndexPath 的转换,这使得更多有趣的用例成为可能。

要了解更多信息,请查看 ComposedUI,它提供了与 UICollectionViewUITableView 配合使用的用户界面实现,允许您从简单的可重用 Section 驱动整个屏幕。