1.MVVM之优雅地开发UITabelView2019
前言:
我是个偏瘦的程序猿,增肥是我的梦想,但自己写的项目中是用MVC的,Controller越来越臃肿,有时候维护起来,不方便。想着用MVVM来减肥。
正文
一、MVVM是什么?
在MVVM设计模式中,组件变成了Model-View-ViewModel。
MVVM有两个规则
- View持有ViewModel的引用,反之没有
- ViewModel持有Model的引用,反之没有

简单来说,View文件import只导入ViewModel.h头文件,ViewModel文件import只导入Model.h头文件,至于Model文件什么都不导入,就是简单的模型。
和MVC相比,表面看起来就是少了Controller,以及多了ViewModel.
实际开发中,UIViewController和View往往是绑定在一起的,UIViewController可以当作一个重量级的View(负责界面切换等)。

不难看出,MVVM是对MVC的扩展,所以MVVM可以完美的兼容MVC。
对于一个界面来说,有时候View和ViewModel往往不止一个,MVVM也可以组合使用:

二、MVVM的优点和缺点
1.优点:在 iOS 开发中实践 MVVM 的话,通常会把大量原来放在 ViewController 里的视图逻辑和数据逻辑移到 ViewModel 里,从而有效的减轻了 ViewController 的负担。另外通过分离出来的 ViewModel 获得了更好的测试性,我们可以针对 ViewModel 来测试,解决了界面元素难于测试的问题。MVVM 通常还会和一个强大的绑定机制一同工作,一旦 ViewModel 所对应的 Model 发生变化时,ViewModel 的属性也会发生变化,而相对应的 View 也随即产生变化。
2.缺点:
1.MVVM 的学习成本和开发成本都很高。MVVM 是一个年轻的设计模式,大多数人对他的了解都不如 MVC 熟悉,基于绑定机制来进行编程需要一定的学习才能较好的上手。同时在 iOS 客户端开发中,并没有现成的绑定机制可以使用,要么使用 KVO,要么引入类似 ReactiveCocoa 这样的第三方库,使得学习成本和开发成本进一步提高。
2.数据绑定使 Debug 变得更难了。数据绑定使程序异常能快速的传递到其他位置,在界面上发现的 Bug 有可能是由 ViewModel 造成的,也有可能是由 Model 层造成的,传递链越长,对 Bug 的定位就越困难。
同时还必须指出的是,在传统的 MVVM 架构中,ViewModel 依然承载的大量的逻辑,包括业务逻辑,界面逻辑,数据存储和网络相关,使得 ViewModel 仍然有可能变得和 MVC 中 ViewController 一样臃肿。
三、优雅地开发UITabelView
model层
1.Person.h文件
#import <Foundation/Foundation.h> @interface Person : NSObject @property (copy, nonatomic) NSString * name; @end
Person.m文件
#import "Person.h" @implementation Person @end
ViewModel层
CustomCellModel.h文件
#import <Foundation/Foundation.h> #import "Person.h"//1.引入model的头文件 @protocol CellActionDispatchable <NSObject>//4.定义协议 @property (copy, nonatomic) NSString * selNameForDidSelected; @end @interface CustomCellModel : NSObject<CellActionDispatchable>//5.遵守s协议 @property (strong, nonatomic) Person * person;//2.定义属性 @property (copy, nonatomic) NSString * selNameForDidSelected;//6.实现协议 - (instancetype)initWithModel:(Person *)person;//3.定义对象方法
+ (NSArray*)queryData;//7.请求后台数据
@end
CustomCellModel.m文件
#import "CustomCellModel.h" @implementation CustomCellModel - (instancetype)initWithModel:(Person *)person{ if (self = [super init]) { self.person = person; self.selNameForDidSelected = @"test";//标识要点击访问的方法名 } return self; } +(NSArray *)queryData{ //模拟请求后台返回的json数据,json转model数组 Person *p = [[Person alloc]init]; p.name = @"张三"; Person *p2 = [[Person alloc]init]; p2.name = @"李四"; return [NSArray arrayWithObjects:p,p2, nil]; } @end
View层
CustomTableViewCell.h文件
#import <Foundation/Foundation.h> #import "CustomCellModel.h"//1.引用viewModel层 @interface CustomTableViewCell : UITableViewCell @property (strong, nonatomic) UILabel * nameLabel; //2.定义UI @property (strong, nonatomic) CustomCellModel * cellModel;//3.定义属性 - (void)bindWithCellModel:(CustomCellModel *)cellModel;//4.定义对象方法,进行数据绑定用的 @end
CustomTableViewCell.m文件
#import "CustomTableViewCell.h" @implementation CustomTableViewCell - (void)bindWithCellModel:(CustomCellModel *)cellModel{ self.cellModel = cellModel; self.nameLabel.text = cellModel.person.name; } //自定义TableViewCell 纯代码 -(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{ if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { UILabel *label = [[UILabel alloc] init]; label.frame = CGRectMake(16,17,72,20); [self addSubview:label]; self.nameLabel = label; } return self; } @end
Controller类
TableViewController.h文件
#import <UIKit/UIKit.h>
@interface TableViewController : UITableViewController
@property (strong, nonatomic)NSArray *dataArray;
@end
TableViewController.m文件
#import "TableViewController.h" #import "CustomTableViewCell.h" #import "CustomCellModel.h" //1.引入viewModel @implementation TableViewController - (void)viewDidLoad { [super viewDidLoad]; self.dataArray = [CustomCellModel queryData];//2.请求数据 [self.tableView registerClass:[CustomTableViewCell class] forCellReuseIdentifier:[CustomTableViewCell className]]; } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.dataArray.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { CustomTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"CustomTableViewCell" forIndexPath:indexPath]; Person * model = [self.dataArray objectAtIndex:indexPath.row]; CustomCellModel * cellModel = [[CustomCellModel alloc] initWithModel:model]; [cell bindWithCellModel:cellModel];//3.数据绑定 return cell; } -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ [tableView deselectRowAtIndexPath:indexPath animated:true]; id<CellActionDispatchable> cellModel =[[CustomCellModel alloc] initWithModel: [self.dataArray objectAtIndex:indexPath.row]]; NSString * selName = cellModel.selNameForDidSelected; [self performSelector:NSSelectorFromString([selName stringByAppendingString:@":"]) withObject:indexPath afterDelay:0.0];//4.避免使用大量的if else去做判断 } -(void)test:(NSIndexPath *)indexPath{ DLog(@"Log=test%ld",indexPath.row);//5.调用相应的方法 } @end
浙公网安备 33010602011771号