FlowKit

整个项目已迁移至新的家目录,现在名为 OWL。

https://github.com/malcommac/Owl

此仓库将在几个月后移除。

--

Version License Platform CocoaPods Compatible Carthage Compatible Twitter

★★ 点亮 Star 以关注项目! ★★
Daniele Margutti 创建 - danielemargutti.com

什么是 FlowKit

FlowKit 是一种创建、填充和管理 UITableViewUICollectionView 的新方法。

通过声明式和类型安全的方法,您不再需要实现 datasource/delegate:您的代码易于阅读、维护,并且符合 SOLID 原则。

想了解更多关于 FlowKit 的信息吗?

我写了一篇介绍性文章:点击此处立即阅读!

功能亮点

您将获得什么

以下代码只是一个简单的示例,展示了如何使用 FlowKit 创建一个简单的联系人列表 UITableView(它在集合视图中的工作方式类似)。

// Create a director which manage the table/collection
let director = TableDirector(self.tableView)

// Declare an adapter which renders Contact Model using ContactCell
let cAdapter = TableAdapter<Contact,ContactCell>()

// Hook events you need
// ...dequeue
cAdapter.on.dequeue = { ctx in
	self.fullNameLabel.text = "\(ctx.model.firstName) \(ctx.model.lastName)"
	self.imageView.image = loadFromURL(ctx.model.avatarURL)
}
// ...tap (or all the other events you typically have for tables/collections)
cAdapter.on.tap = { ctx in
	openDetail(forContact: ctx.model)
}

// Register adapter; now the director know how to render your data
director.register(adapter: cAdapter)

// Manage your source by adding/removing/moving sections & rows
director.add(models: arrayOfContacts)
// Finally reload your table
director.reloadData()

非常简单,不是吗?

无需 datasource,无需 delegate,只需声明式语法即可轻松且类型安全地创建和管理您的数据(模型和单元格)。通过阅读指南的其余部分,了解更多关于节、页眉/页脚和事件的信息。

目录

文档

以下指南通过一个真实示例解释了如何使用 FlowKit 中可用的功能。如果您想查看实时示例,请打开 FlowKit.xcodeproj 并运行 Example 应用程序。

注意以下概念即使在使用 FlowKit 处理表格或集合时也有效(使用的每个类都以 Table[...]Collection[...] 前缀开头,并且在函数之间存在相似之处时,函数/属性的名称是一致的)。

概述

下图描述了 Collection 的 FlowKit 基础架构(相同的图表也适用于表格)。

FlowKit 最重要的类是 Director;此类(表格的 TableDirector,集合的 CollectionDirector/FlowCollectionDirector)管理数据和 UI 之间的同步:您可以添加/删除/移动节,并直接从此实例配置列表的外观和行为。使用 FlowKit 的第一步是将 director 分配给您的列表:您可以通过调用 list.director 或仅使用要管理的列表实例创建自己的 director 来完成此操作

let director = FlowCollectionDirector(self.collectionView)

为了渲染某些数据,FlowKit 必须知道您想在列表中显示哪种类型的数据;数据以 <模型,视图> 对的形式组织(其中 模型 是您想要添加到表中的对象,而视图是用于表示数据的单元格)。模型 必须是符合 ModelProtocol 的对象(类或结构体):这是一个简单的协议,要求存在属性 modelID。此属性用于唯一标识模型并在自动重载动画期间评估项目之间的差异。

适配器还允许接收用于配置视图和行为的事件:您可以拦截模型的实例的点击事件并执行某些操作,或者仅使用模型实例填充接收到的类型安全单元格实例。

因此,作为第二步,您需要注册一些适配器

let adapter = CollectionAdapter<Contact,ContactCell>()
adapter.on.tap = { ctx in
	print("User tapped on \(ctx.model.firstName)")
}
adapter.on.dequeue = { ctx in
	ctx.cell?.titleLabel?.text = ctx.model.firstName
}

现在您可以准备创建包含模型的节了

let section = TableSection(headerTitle: "Contacts", models: contactsList)
self.tableView.director.add(section: section)

模型数组可以是异构的,只需记住使您的对象符合 ModelProtocolHashable 协议,并注册关联的适配器。FlowKit 将负责在需要时调用您的适配器事件。

最后,您可以重新加载数据

self.tableView.reloadData()

瞧!只需几行代码,您就可以创建和管理甚至复杂的列表。

以下指南描述了库的所有其他功能。

创建 Director (TableDirector/CollectionDirector)

您可以将 Director 视为表格/集合的所有者/管理器:使用它,您可以声明您的滚动视图能够显示的数据类型(模型和视图/单元格),添加/删除/移动节和节中的项目。自从您开始使用 FlowKit 以来,您将使用 director 实例来管理 UI 的内容。

请记住:单个 director 实例只能管理滚动视图的单个实例。

您有两种设置 director 的方法;显式地

public class ViewController: UIViewController {
	@IBOutlet public var tableView: UITableView?
	private var director: TableDirector?
	
	override func viewDidLoad() {
		super.viewDidLoad()
		self.director = TableDirector(self.tableView!)	
	}
}

或隐式地使用,通过访问 director 属性:第一次调用它时,会创建一个新的 director 实例,并使用对表格/集合实例的强引用进行分配。

注意: 对于 UICollectionView,会自动创建 FlowCollectionDirector;如果您使用其他布局,则必须手动创建一个新的。

let director = self.tableView.director // create a director automatically and assign as strong reference to the table/collection
// do something with it...

注册适配器 (TableAdapter/CollectionAdapter)

一旦您有了 director,您就需要告诉它您将要渲染哪种类型的数据:您的滚动视图中可以有异构的模型和视图(单元格)集合,但单个模型只能渲染为一种类型的视图。

假设您想要渲染两种类型的模型

您将需要两个适配器

let contactAdpt = TableAdapter<Contact,ContactGroup>()
let groupAdpt = TableAdapter<ContactGroup,ContactGroupCell>()
tableView.director.register(adapters: [contactAdpt, groupAdpt])

现在您可以准备好呈现您的数据了。

创建数据模型 (ModelProtocol)

为了渲染您的数据,滚动视图的每个对象都必须符合 ModelProtocol,这是一个简单的协议,要求实现 modelID 属性(一个 Int)。此属性用于唯一标识模型并在自动重载动画期间评估项目之间的差异。对于基于类的对象 (AnyObject),可以使用 ObjectIdentifier() 获得此属性的默认实现。相反,对于基于值的对象(即结构体),必须提供显式实现。

这是 Contact 模型的示例实现

public class Contact: ModelProtocol {
	public var name: String
	public var GUID: String = NSUUID().uuidString

	public var id: Int {
		return GUID.hashValue
	}
	
	public static func == (lhs: Contact, rhs: Contact) -> Bool {
		return lhs.GUID == rhs.GUID
	}
		
	public init(_ name: String) {
		self.name = name
	}
}

创建单元格 (UITableViewCell/UICollectionViewCell)

UITableViewCellUICollectionViewCell 及其子类都自动符合 CellProtocol

唯一的约束是关于 reuseIdentifier单元格必须具有 reuseIdentifier(Interface Builder 中的 Identifier),即类本身的名称。

如果需要,您可以通过覆盖单元格的 reuseIdentifier: String 属性并返回您自己的标识符来覆盖此行为。

单元格可以通过三种方式加载

这是一个 ContactCell 的小例子

public class ContactCell: UITableViewCell {
	@IBOutlet public var labelName: UILabel?
	@IBOutlet public var labelSurname: UILabel?
	@IBOutlet public var icon: UIImageView?
}

添加节 (TableSection/CollectionSection)

每个表格/集合都必须至少有一个节才能显示内容。TableSection/CollectionSection 实例保存要显示的项目(在 .models 属性中),以及您可以应用的任何可选页眉/页脚。

为了管理表格的节,您需要使用父 Director 的以下方法

以下示例创建一个新的 TableSection,其中包含一些项目,一个基于 String 的页眉,然后将其附加到表格的末尾。

let section = TableSection(headerTitle: "The Strangers", items: [mrBrown,mrGreen,mrWhite])
table.director.add(section: section)

管理节中的模型/项

对于节,相同的 add/remove/move 函数也适用于 models 数组,该数组描述每个节(TableSection/CollectionSection 实例)内的内容(行/项目)。

这是完整列表

在任何更改之后,您必须从主线程调用 director.reloadData() 函数来更新 UI。

这是一个项目管理的示例

let section = self.tableView.director.firstSection()

let newItems = [Contact("Daniele","Margutti"),Contact("Fabio","Rossi")]
section.add(models: newItems) // add two new contacts
section.remove(at: 0) // remove first item

// ...
self.tableView.director.reloadData() // reload data

设置页眉和页脚 (TableSectionView/CollectionSectionView)

简单页眉/页脚

节可以有或没有页眉/页脚;这些可以是简单的 String(如您在上面看到的)或自定义视图。

设置简单页眉非常简单,只需设置 headerTitle/footerTitle

section.headerTitle = "New Strangers"
section.footerTitle = "\(contacts.count) contacts")

自定义视图页眉/页脚

要使用自定义视图作为页眉/页脚,您需要创建一个自定义 xib 文件,其中包含 UITableViewHeaderFooterView(用于表格)或 UICollectionReusableView(用于集合)视图子类作为根项。

以下示例显示了自定义页眉以及如何设置它

// we also need of TableExampleHeaderView.xib file
// with TableExampleHeaderView view as root item
public class TableExampleHeaderView: UITableViewHeaderFooterView {
	@IBOutlet public var titleLabel: UILabel?
}

// create the header container (will receive events)
let header = TableSectionView<TableExampleHeaderView>()
// hooks any event. You need at least the `height` as below
header.on.height = { _ in
	return 150
}

// Use it
let section = TableSection(headerView: header, items: [mrBrown,mrGreen,mrWhite])
table.director.add(section: section)

重新加载带/不带动画的数据

对数据模型的每次更改都必须通过调用节和项目级别上可用的 add/remove/move 函数来完成。更改后,您需要调用 director 的 reloadData() 函数来更新 UI。

以下示例在进行一些更改后更新表格

// do some changes
tableView.director.remove(section: 0)
tableView.director.add(section: newSection, at: 2)
...
tableView.director.firstSection().remove(at: 0)

// then reload
tableView.director.reloadData()

如果您需要执行动画重新加载,只需在方法中可用的回调中对模型进行更改即可。动画将自动评估和应用!

tableView.director.reloadData(after: { _ in
	tableView.director.remove(section: 0)
	tableView.director.add(section: newSection, at: 2)
	
	return TableReloadAnimations.default()
})

对于 TableDirector,您必须提供一个 TableReloadAnimations 配置,该配置定义了每种类型的更改(插入/删除/重新加载/移动)必须应用哪种类型的 UITableViewRowAnimationTableReloadAnimations.default() 仅对每种类型使用 .automatic(您也可以实现自己的对象,该对象需要符合 TableReloadAnimationProtocol 协议)。

对于 CollectionDirector,您无需返回任何内容;评估是根据使用的布局为您进行的。

监听事件

所有事件都可以从其各自的对象(从 .on 属性开始)进行挂钩。FlowKit 中提供了所有标准表格和集合事件;事件的名称与其在 UIKit 中的标准对应项相似(有关任何特定事件的更多信息,请参阅官方文档)。

单元格尺寸调整

FlowKit 支持使用自动布局轻松调整单元格大小。您可以根据适配器或集合来设置单元格的大小。对于自动布局驱动的单元格尺寸调整,将 rowHeight(对于 TableDirector)或 itemSize(对于 CollectionDirector/FlowCollectionDirector)设置为 autoLayout 值,然后提供一个估计值。

接受的值包括

安装

通过 CocoaPods 安装

CocoaPods 是一个依赖项管理器,它可以自动化和简化在您的项目中使用 FlowKit 等第三方库的过程。您可以使用以下命令安装它

$ sudo gem install cocoapods

构建 FlowKit 需要 CocoaPods 1.0.1+。

通过 Podfile 安装

要使用 CocoaPods 将 FlowKit 集成到您的 Xcode 项目中,请在您的 Podfile 中指定它

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'

target 'TargetName' do
use_frameworks!
pod 'FlowKitManager'
end

然后,运行以下命令

$ pod install

Carthage

Carthage 是一个去中心化的依赖项管理器,它可以构建您的依赖项并为您提供二进制框架。

您可以使用 Homebrew 通过以下命令安装 Carthage

$ brew update
$ brew install carthage

要使用 Carthage 将 FlowKit 集成到您的 Xcode 项目中,请在您的 Cartfile 中指定它

github "malcommac/FlowKitManager"

运行 carthage 以构建框架,并将构建的 FlowKit.framework 拖到您的 Xcode 项目中。

要求

FlowKit 与 Swift 4.x 兼容。