ComposedUI 基于 Composed 构建,增加了用户界面功能,允许你驱动应用程序中的屏幕。

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

该库由每种视图类型的 4 个关键类型组成,以及用于提供可选功能的各种协议。

UICollectionView 的实现以 Collection 为前缀,UITableView 的实现以 Table 为前缀,UIStackView 的实现以 Stack 为前缀。

例如,UICollectionView 的类型定义如下:

CollectionSectionProvider 为了使你的 Section 能够在 UICollectionView 中使用,你的 Section 需要遵守此协议。 它只有一个要求,即返回 CollectionSection 的函数。

CollectionSection 此类型封装了 3 个 CollectionElement 实例。 一个 cell,以及可选的 header 和 footer 元素。

CollectionElement 一个 element 定义了 cell 或 supplementary view 应该如何注册、出列和配置以进行显示。

CollectionCoordinator Coordinator 负责协调 provider(通过其映射)和其视图之间的所有事件。 它通常是相应视图的 delegate & dataSource,以及根 provider 的 updateDelegate

开始使用

让我们定义一个简单的 Section 来保存我们的联系人

struct Person {
	var kind: String // family or friend
}

final class ContactsSection: ArraySection<Person> { }

现在我们可以扩展它,以便在 collection view 中显示它

extension ContactsSection: CollectionSectionProvider {

	func section(with traitCollection: UITraitCollection) -> CollectionSection {
		/*
		Notes:
		The `dequeueMethod` signals to the coordinator how to register and dequeue the cell
		The element is generic to that cell type
		*/
		let cell = CollectionCellElement(section: self, dequeueMethod: .fromNib(PersonCell.self)) { cell, index, section in
			// Since everything is generic, we know both the cell and the element types
			cell.prepare(with: element(at: index))
		}

		return CollectionSection(section: self, cell: cell, header: header)
	}
	
}

最后,我们需要在我们的 view controller 上保留一个 coordinator

final class ContactsViewController: UICollectionViewController {
	
	private var coordinator: CollectionCoordinator?
	
	override func viewDidLoad() {
		super.viewDidLoad()
		
		let contacts = ContactsSection()
		// contacts.append(...)
		
		// this single line is all that's needed to connect a collection view to our provider
		coordinator = CollectionCoordinator(collectionView: collectionView, sections: contacts)
	}

}

现在,如果我们构建并运行,我们的 collection view 应该会按照预期填充我们的联系人。 就这么简单!

协议

ComposedUI 还包括各种协议,用于为你的 Section 启用可选行为。 让我们为上面的 Section 添加对选择事件的支持

extension ContactsSection: CollectionSelectionHandler {
	
	func didSelect(at index: Int, cell: UICollectionViewCell) {
		print(element(at: index))
		deselect(at: index)
	}
	
}

就是这样! 我们的 coordinator 已经处理了选择,因此当发生选择时,它使用 indexPath 来确定选择发生在哪个 Section 中,然后尝试将该 Section 转换为协议,并在成功时调用与我们关联的方法。 如你所见,这是一种极其强大的方法,但 API 极其简单优雅,具有两个主要优点:

  1. 你可以选择你想要的功能,而不是默认继承它们
  2. 你可以提供自己的协议,并使用 Composed 提供的相同基础设施

高级用法

到目前为止,我们已经构建了一个相对简单的示例,显示了一个 Section。 让我们更新上面的 view controller 以使用 SectionProvider - 并让事情变得更有趣。

override func viewDidLoad() {
	super.viewDidLoad()
	
	// ... create our contacts (family and friends)
	
	let provider = ComposedSectionProvider()
	provider.append(family)
	provider.append(friends)
	
	// this single line is all that's needed to connect a collection view to our provider
	coordinator = CollectionCoordinator(collectionView: collectionView, provider: provider)
}

如果我们现在再次运行我们的示例,我们会看到一切都像以前一样工作,除了我们现在有两个 Section。

这已经有很多好处:

  1. 我们不需要管理 indexPaths 或 section indexes
  2. 我们能够重用我们现有的 Section
  3. 我们的 Section 不知道它现在位于更大的结构中

现在让我们添加一些自定义行为,具体取决于数据

extension ContactsSection: CollectionSelectionHandler {
	var allowsMultipleSelection: Bool { return isFamily }
}

让我们再次运行该项目,我们可以看到 Family Section 现在允许多重选择,而 Friend Section 则不允许。 这是使用 ComposedUI 的另一个巨大好处,因为 Coordinator 能够执行更高级的逻辑,而无需了解底层结构。