MVVM的定义:
①. Model- model 在 MVVM 中没有真正的变化. 取决于你的偏好, 你的 model 可能会或可能不会封装一些额外的业务逻辑工作. 我更倾向于把它当做一个容纳表现数据-模型对象信息的结构体, 并在一个单独的管理类中维护的创建/管理模型的统一逻辑.
②. View- view 包含实际 UI 本身(不论是UIView代码, storyboard 和 xib), 任何视图特定的逻辑, 和对用户输入的反馈. 在 iOS 中这不仅需要UIView代码和那些文件, 还包括很多需由UIViewController处理的工作.
③. View-Model- 这个术语本身会带来困惑, 因为它混搭了两个我们已知的术语, 但却是完全不同的东东. 它不是传统数据-模型结构中模型的意思(又来了, 只是我喜欢这个例子). 它的职责之一就是作为一个表现视图显示自身所需数据的静态模型;但它也有收集, 解释和转换那些数据的责任. 这留给了 view (controller) 一个更加清晰明确的任务: 呈现由 view-model 提供的数据.
View-Model 的总结:
view-model一词的确不能充分表达我们的意图. 一个更好的术语可能是 “View Coordinator”(感谢 Dave Lee 提的这个 “View Coordinator” 术语, 真是个好点子). 你可以认为它就像是电视新闻主播背后的研究人员和作家团队. 它从必要的资源(数据库, 网络服务调用, 等)中获取原始数据, 运用逻辑, 并处理成 view (controller) 的展示数据. 它(通常通过属性)暴露给视图控制器需要知道的仅关于显示视图工作的信息(理想地你不会暴漏你的 data-model 对象). 它还负责对上游数据的修改(比如更新模型/数据库, API POST 调用)
MVC 中的 MVVM:
为了图解表示, 我们颠倒了MVC中的V和C, 于是首字母缩写更能准确地反映出组件间的关系方位, 给我们带来MCV. 我也会对MVVM这么干, 将V(iew)移到VM的右边最终成为了MVMV. (我相信这些首字母缩写起初不排成这样更合理的顺序是有原因的. )
下面让我们来看个简单的映射:
MVC和MVVM的映射a. 我试图遵循区块尺寸(非常)大致对应它们负责的工作量.
b. 你可以看到我们巨大的视图控制器和 view-model 之间有大块工作上的重合.
c. 你也可以看看视图控制器在 MVVM 中的足迹有多大一部分是跟视图重合的.
我们试图将重合的那块工作区域剥离到 view-model 中, 并让视图控制器的更加简洁。
实际上最终以MVMCV告终.ModelView-ModelControllerView作为结点。
融合现在视图控制器仅关注用 view-model 的数据配置和管理各种各样的视图, 并先于用户输入时让 view-model 获知并需要向上游修改数据. 视图控制器不需要了解关于网络服务调用, Core Data, 模型对象等. (事实上有时通过 view-model 头文件而不是复制一大堆属性来暴漏 model 是很务实的, 后面还会有)
view-model 会在视图控制器上以一个属性的方式存在. 视图控制器知道 view-model 和它的公有属性, 但是 view-model 对视图控制器一无所知.
帮助你理解我们如何把组件组装在一起还有组件对应职责的另一种方式, 就是着眼于我们新的应用构建模块层级图.
对应关系图View-Model 和 View Controller 在一起,但独立
我们来看个简单的 view-model 头文件来,以便对你有个更好地概念. 为了情节简单, 我们构建按了一个伪造的推特客户端来查看任何推特用户的最新回复, 通过输入他们的姓名并点击 “Go”. 我们的样例界面将会是这样:
a. 有一个让用户输入他们姓名的UITextField, 和一个写着 “Go” 的UIButton
b. 有显示被查看的当前用户头像和姓名的UIImageView和UILabel各一个
c. 下面放着一个显示最新回复推文的UITableView
d. 允许无限滚动
这里的view-model因该这样定义:view-model 暴漏视图控制器所必需的最小量信息, 视图控制器实际上并不在乎 view-model 是如何获得这些信息的. 现在我们两者都不在乎. 仅仅假定你习惯于标准的网络服务请求, 校验, 数据操作和存储.
view-model 不做的事:对视图控制器以任何形式直接起作用或直接通告其变化
View Controller要做的事:
视图控制器从 view-model 获取的数据将用来:
当 usernameValid 的值发生变化时触发 “Go” 按钮的 enabled 属性
当 usernameValid 等于 NO 时调整按钮的 alpha 值为0. 5(等于 YES 时设为1. 0)
更新 UILable 的 text 属性为字符串 userFullName 的值
更新 UIImageView 的 image 属性为 userAvatarImage 的值
用 tweets 数组中的对象设置表格视图中的 cell (后面会提到) 当滑到表格视图底部时如果 allTweetsLoaded 为 NO , 提供一个 显示 “loading” 的 cell
视图控制器不做的事:
发起网络服务调用
管理tweets数组
判定username内容是否有效
将用户的姓和名格式化为全名
下载用户头像并转成UIImage(如果你习惯在UIImageView上使用类别从网络加载图片, 你可以暴漏 URL 而不是图片. 这样没有让 view-model 和 UIKit 更完全摆脱, 但我视UIImage为数据而非数据的确切显示. 这些东西不是固定死的. )
注意:视图控制器总的责任是处理 view-model 中的变化.
视图控制器将对 view-model 起如下作用:
每当UITextField中的文本发生变化, 更新 view-model 上仅有的readwrite属性username
当 “Go” 按钮被按下时调用 view-model 上的getTweetsForCurrentUsername方法
当到达表格中的 “loading” cell 时调用 view-model 上的loadMoreTweets方法
子 view-model的作用:
使用 view-model 上的tweets数组中的对象配置表格视图的 cell. 通常你会期待展现tweets的是数据-模型对象. 你可能已经对其感到奇怪, 因为我们试图通过 MVVM 模式不暴漏数据-模型对象. (前面提到过的)
view-model 不必在屏幕上显示所有东西.你可用子 view-model 来代表屏幕上更小, 更潜在被封装的部分. 如果一个视图上的一小块儿(比如表格的 cell)在 app 中可以被重用以及(或)表现多个数据-模型对象, 子 view-model 会格外有利
View-Model 产生 View-Model
严格来说, 你应该为 app delegate 中的顶级视图控制器创建一个 view-model. 当展示一个新的视图控制器时, 或很小的视图被 view-model 表现时, 你应要求当前的 view-model 为你创建一个子 view-model.
假如我们想要在用户轻拍应用顶部的头像时添加一个资料视图控制器. 我们可以为一级 view-model 添加类似如下方法:
- (MYTwitterUserProfileViewModel *) viewModelForCurrentUser;
然后在我们的一级视图控制器中这么用它:
在这个例子中我将会展现当前用户的资料视图控制器, 但是我的资料视图控制器需要一个 view-model. 我这的主视图控制器不知道用于创建关联相关用户 view-model 的全部必要数据, 所以它请求它自己的 view-model 来干这种创建新 view-model 的苦差事.(这就助长和造成了逻辑混乱,代码烦乱的开始)
View-Model 列表
至于我们的推特 cell, 当数据驱动屏幕(在这个例子中或许是通过网络服务调用)聚到一起时, 我将会代表性地提前为对应的 cell 创建所有的 view-model. 所以在我们这个方案中,tweets将会是一个MYTweetCellViewModel对象数组. 在我的表格视图中的cellForRowAtIndexPath方法中, 我将会在正确的索引上简单地抓取 view-model, 并把它赋值给我的 cell 上的 view-model 属性.
网友评论