HubFramework 的组件化实现

HubFramework 是 spotify 公司开源的一套组件驱动 UI 框架,可快速构建 UI,可定制化程度较高,同时也可以很轻易地用 Json 数据来动态构建 UI。

HubFramework 是用 Objective-C 写的,同时它完全支持 Swift,其 demo 也是完全由 Swift 实现。

最近 Instagram 刚开源 IGListKit 也是完全用 Objective-C,demo 由 Swift 完成。

要入手 HubFramework,需要了解它的五大模块,分别为 Action、Component、Content、Layout 和 JSON。本文以介绍前四者为主。

Action

image

自己粗劣地画了张图,大致能展现整个事件响应过程。

事件输入

类似 textField 和 button 的响应事件都会由 actionDelegate 传递给 ViewControllerImplementation 来处理。

而集合视图的代理直接是 ViewControllerImplementation,比如常见的 didSelectItemAtIndexPath:。因而 cell 的点击事件直接由 ViewControllerImplementation 处理。

实际上,大部分的 Components 都是添加在 CollectionViewCell 上的,这里并不存在我们常用的 UITableView,而是直接由 UICollectionView 代替实现。

事件处理与响应

ViewControllerImplementation 收到事件便会将各种数据封装成 Context,然后交给 ActionHandler 处理。

通过 Context 可以拿到的数据不仅仅只有 ComponentModel 和 ViewURI,还包括 ViewModel, ViewController 和自定义的 CustomData。

因此 ActionHandler 能处理的操作范围就大了。

ActionHandler 默认只有一个。当 action 和 target 都存在的时候会执行 URI 跳转。

官方建议尽量通过系统 OpenURL 来处理,同时也是为了支持远程调用,包括 deep link。

但是对于非常规对象,就不可能那通过 OpenURL 来完成。那么这时候可以通过自定义 action 来实现。

另一方面,除了 URI 跳转,组件也需要同步更新。比如 searchBar 的输入,于是请求数据,刷新视图。这时候只要组件遵守协议 HUBContentOperationActionObserver,也会收到响应事件。

如果同一个页面的有多个组件都遵守这个协议,那么就都会受到响应事件,这时候就需要根据 customActionIdentifier 来区分。

Component

Component 大致分为 header,body,overlays。body 主要由框架的 collectionView 实现,overlays 主要为 loadingView 等。

Component 在收到 configureViewWithModel: 消息进行渲染,官方推荐尽量用 HUBComponentModel 协议预设的 property,基本可以满足大部分需求。除非需要定制化才使用 customData。

其中 ComponentModel 还包含 target 对象,用于指定对应的 URI 跳转。

ContentOperation

一个 ContentOperationFactory 带有多个 ContentOperation,而这些 ContentOperation 的顺序也决定了内容被加载的顺序。

比如,有这么些 ContentOperations:

contentOperationFactory1
- contentOperationA1
- contentOperationB1
contentOperationFactory2
- contentOperationA2
- contentOperationB2

那么他们的加载顺序是

@[contentOperationA1, contentOperationB1, contentOperationA2, contentOperationB2];

前者加载完毕,后者才会继续加载。

既然顺序定好了,便可以制定它的一些特性:

  • 当某个 contentOperation 调用 reschedul,会刷新组件,排在它之后的每个 contentOperation 也会 reschedul,而前面的不会;
  • 多个 contentOperations 是公用一个 ViewModelBuilder 的,这使得后者可以获得并修改前者配置的数据;
  • ViewModelBuilder 在 contentOperations 中每传递一次,就会保存一份。这使得即使前者重新加载,获得的 ViewModelBuilder 也不包含后者所修改的。

Layout

layout 主要由多个 Layout trait 和一个 Layout manager 组成。

Layout trait 用来表示组件间、组件与父组件是否应该有间距。比如 HUBComponentLayoutTraitFullWidth 表示左右都不要间距,HUBComponentLayoutTraitStackable 表示组件之间都同意才不要间距。一个组件可以返回多个 LayoutTrait 值。

Layout manager 则根据 Layout trait 来返回需要的间距。一般情况下,这个 manager 是全局的,统一定义了每个组件的间距。如果有更多的特定情况,也可以通过增加 Layout Trait 类型来处理。

总结

iOS 开发发展到现在,各种基础库日趋完备。如何快速构建 UI,处理复杂交互,提高代码的可维护性与可测试性,仍是当前值得探索的问题。

HubFrameWork 是一个很强大的框架,真个过程中你感觉不到 ViewController 的存在,也就不存在臃肿控制器的问题。业务代码都被分配到各自的 contentOperation 中执行,通过面向协议组合起来,很适合在多人开发中应用。其次,对组件进行标准化之后,便可以很方便地通过 JSON 动态更新 UI。

另一方面来看,它具有一定的学习成本;大范围使用时需要注册较多 Feature 和 Component,内存占用问题需要考量。