英文作者Colin Eberhardt,原文可查看BINDING TO A UITABLEVIEW FROM A REACTIVECOCOA VIEWMODEL
这篇博客介绍了一个工具类,这个类将ReactiveCocoa
中的ViewModels
绑定到UITableView
,而不需要通常的datasource和delegate。下面是这个辅助类的使用方法:
|
|
介绍
我总是在不断的编写代码:在工作中,在家里,在火车上…如果我不写代码,我就会觉得不快乐!(注:这才是真正的程序员啊)
在过去的几个月中,我开始在我的工程中越来越多地使用ReactiveCocoa了。这个框架可以用来创建一些非常优雅的解决方案,但同时它非常具有挑战性,因为对于任何一个问题,都有许多可用的解决方案。对于像我这样的编码狂人来说,这再好不过了。
几个月之前,我在Ray Wenderlich的网站上发表了两篇关于ReactiveCocoa的文章(第一部分、第二部分),以及一个Tech Talk视频。这些覆盖了ReactiveCocoa的基本用法,希望能让广大读者熟悉ReactiveCocoa。不过,我收到不少请求,希望能讨论一些使用ReactiveCocoa实现MVVM模式的高级话题。
正因此,我开始写这篇文章。不过,在我发布之前,我想先分享一个已纠缠我很久的问题…
如果将一个UITableView
绑定到一个ReactiveCocoa
的ViewModel
中?
视图模式
我以一个简单的例子开头–一个允许我们搜索Twitter的ViewModel
:
|
|
这个ViewModel
的实现重用了我在ReactiveCocoa
指南第二部分所创建的信号,所以我不在此重复。如果想要看详细的代码,可以在github上查找。
将ViewModel
绑定到一个带有UITextField
和UIButton
的UI是使用ReactiveCocoa
最普通不过工作了
|
|
在上面的代码中,当点击go按钮时,我们处理了诸如隐藏键盘这样的操作,并将网络连接的activity indicator
绑定到了searchCommand.executing
信号。
这样就将ViewModel
三个属性中的两个绑定到了UI,到目前为止,一切都还不错!
最后一个属性是searchResults
;这个属性是一个数组,包含了搜索结果。我们可以通过RACObserve
来观察这个属性的修改,RACObserve
创建了一个信号,该信号会在每次更新时发出一个next事件。但不幸的是,我们不能只给UITableView
一个对象的数组,并告诉它去渲染自己。
如果我们在StackOverflow上搜索相关帖子,或者查看别人的ReactiveCocoa
实例,可以看到传统的方式似乎是我们需要自己去实现table view
的代理和数据源。换句话说,我们之前优雅的只需要几行绑定代码的视图类代码会由于需要实现table view
的各种逻辑而显示异常丑陋。
不过,我们有更好的方法。
一个Table View绑定辅助类
在MVVM模式中,每一个View都由一个ViewModel
支撑着。一个视图可能占据整个屏幕(此时我们将一个视图控制器绑定到一个ViewModel
),或者只占据屏幕的一部分。
我们的顶层ViewModel
的searchResults
属性包含了一个对象数组,其中每一个元素都是一个ViewModel
。为了解决这个问题,我们需要的是一个通用的机制来为每个视图创建一个ViewModel
,并将这两者绑定在一起。
Nib提供了一种便捷的机制来定义可重用的视图。可以方便地使用nib来定义一个table view的单元格。
一个合理的table view绑定辅助类的接口如下:
|
|
这个绑定类使用提供的table view来渲染由源信号所提供的view model,另外templeteCell定义了视图。让我们来看看这个辅助类的实现:
|
|
注意,初始化方法是内在逻辑所在。在这里,sourceSignal
添加了一个subscriber
,这样每次ViewModel
的数组属性变化时,当前属性值的引用都会被保存,而table view也会重新加载。同样,也会创建templeteCell
实例,来确定单元格的高度。
最后,这个类实现了table view的数据源方法,并通过信号来获取数据。
其中,单元格Cell必须实现以下协议,该协议提供了一个信号方法来将Cell绑定到相应的ViewModel
上。
|
|
将这个用于实际当中,现在只需要几行代码就可以将一个数组属性绑定到一个table view上了。
|
|
注意,源信号是通过RACObserver
宏来创建的。这个信号在每次属性通过setter来改变都会发出一个next事件。
cell的实现类似于视图控制器;它们的UI控件定义在一个nib文件中并连接到相应的outlet属性。下图是该示例程序中定义cell的nib:
定义在CEReactiveView
协议中的ViewModel
绑定方法实现如下:
|
|
注意,由于CETweetViewModel
的属性不会发生变化,因此它们的值直接被拷贝到相应的UI控件上。当然,如果它们的值会改变,我们也可以使用ReactiveCocoa
来将两者绑定到一起。
cell的实现同样使用了ReactiveCocoa
在后台加载图片:
|
|
通过这种方式,我们就可以让我们的视图控制器保持少量的代码。看,是不是很整洁。
下面是完整的程序的实现效果:
处理选中事件
当前的绑定辅助类允许我们在一个table view中渲染ViewModel
的数组,但如果我们需要处理选中事件呢?传统的方法是在视图控制器的手动处理,实现table view的代理方法,并执行相关的ViewModel
的命令。
不过,这部分逻辑代码也可以放入到绑定辅助类中。
首先,我们在初始化方法中添加一个选择命令:
|
|
这个初始化方法的实现现在存储了这个命令的引用。辅助类同样也实现了table view的代理,即tableView:didSelectRowAtIndexPath:
方法的实现如下:
|
|
即当命令被调用时,会将选择的ViewModel
作为执行参数传入。
在顶层ViewModel
中,我已经添加了一个命令,这个操作只是简单地记录一下日志:
|
|
结论
希望这个table view绑定辅助类能够帮助那些使用MVVM
和ReactiveCocoa
来开发iOS应用的开发者们。所有的代码都在github上。如果您有任何意见、想法或建议,请让我知道。