mvc模式的缺点。
1.c层(viewController层),代码冗余,不方便阅读。一般包括以下几点:
1)界面构建。
2)网络数据的请求和后续处理。
3)响应逻辑。比如对点击事件的处理、很多delegate方法。
4)数据源方法。比如常用的tableView会有datasource方法,其中数cellForRowAtIndexpath最为典型,其中数据展示前的代码也是非常的多。
举个例子:在使用tableView时,section、row的判断很头疼,点击时间的if 、switch判断更加让人烦躁。如:
不加注释的行为简直受不了!
其次,代码不够灵活,后期难以维护。想要增加、删除的功能时,不仅要修改数据源(self.storeArray)中的数据,还要修改tableView中返回的section,row的数量,还需要修改cellForRow中,view的赋值,和对cell点击事件的处理,感觉无从下手。
最后,c层代码太多,仅仅一个界面就达到了1,2千行左右的代码,寻找很不方便。(尤其是不加注释!)
那么接下来,我们主要介绍下传说的MVVM模式,即Model +View +ViewModel。其实相比于MVC,重点在于ViewModel,我的理解就是,将我们对传统的Model进行继承和重用。下面通过一个例子来说说。
首先,我们可以将界面根据cell类型,去分几大类。如,该界面中,第一类"地址"cell,第二类"药店"cell,第三类''其他''cell(都的左侧、右侧都是文本型)。
这样我们就可以就只需要3个model,来分别储存在3中cell的数据。但是这3中model也有很多通用的数据,如cell的种类,cell的高度,cell是否要显示,cell是否能点击等等。这就需要一个基类,来存放这些通用的属性。上述的3个model来继承这个基类,来实现model的重用。
如:我们通过建立SubmitOrderBaseModel为基类
@interfaceSubmitOrderBaseModel :NSObject
/**
cell高度
*/
@property(nonatomic,assign)CGFloatcellHeight;
/**
cell类型
*/
@property(nonatomic,copy)NSString* type;
/**
model名称
*/
@property(nonatomic,copy)NSString* title;
/**
是否显示
*/
@property(nonatomic,assign)BOOLisShow;
/**
是否能点击
*/
@property(nonatomic,assign)BOOLisSelected;
/**
子model
*/
@property(nonatomic,strong)NSMutableArray*models;
@end
一定要加注释!
"地址"cell,SubmitOrderAddressModel,继承SubmitOrderBaseModel
@interface SubmitOrderAddressModel : SubmitOrderBaseModel
@end
"药店"cell,SubmitOrderShopModel,继承SubmitOrderBaseModel
@interface SubmitOrderShopModel : SubmitOrderBaseModel
@end
其他cell,SubMitOrderNormalModel,继承SubmitOrderBaseModel
@interface SubMitOrderNormalModel :SubmitOrderBaseModel
/**
右侧文字输入方向
*/
@property(nonatomic,strong)NSString *rightTextAlignment;
/**
右侧字体颜色
*/
@property(nonatomic,strong)UIColor *rightColor;
@end
''其他''cell中又有写微小的区别,比如右侧文本的颜色和输入方向,这是''其他''cell中的独有属性。
好,现在我们可以在ViewController中,根据界面的需求来处理数据。在上面的''界面''中,我们分析了需要3种model来储存数据,但是他根据业务逻辑分了6个(其实是大于6个,一个药店一个section)section。这样的话,可以根据section的个数来处理数据。创建个一个大数组(orderTypeArray),其中储存了我们所要展示的所有数据,他的个数就是section的个数。
在处理数据时,我遇到的问题是,“其他”cell,type都是一种属性,你要在models属性中添加要展示的cell数据,封装成一种数据格式,比如我封装成字典以"NAME"为key,方便赋值时使用。
整理后的数组大概是这个样子的:
现在拿到数组后,就可以去赋值了。首先我们把tableView的delegate拿出来,独立成一个类,然后在tableView设置代理和数据源时,绑定在个类。
_delegate = [[SubmitOrderTableViewDelegate alloc] init];
_mainTableView.delegate =_delegate;
_mainTableView.dataSource =_delegate;
要记得在SubmitOrderTableViewDelegate.h中遵守UITableViewDataSource,UITableViewDelegate。
然后我们就可以在SubmitOrderTableViewDelegate.m中实现要展示的数据了。
1) 在numberOfSectionsInTableView方法中,返回当然是数组(orderTypeArray)中model的个数了。
return _orderTypeArray.count;
2)在numberOfRowsInSection方法中,返回的是每个model中models中的对象个数。
SubmitOrderBaseModel *model =_orderTypeArray[section];
if ([model.type isEqualToString:@"OrderAddressTableViewCell"] || [model.type isEqualToString:@"GoodsShopTableViewCell"] )//地址、药品只返回1
{
return 1;
}
return model.models.count;
3)在heightForRowAtIndexPath方法中,返回的是我们model.cellHeight参数。
SubmitOrderBaseModel *model =_orderTypeArray[indexPath.section];
CGFloat height =0;
if (!model.cellHeight)
{
SubmitOrderBaseModel *subModel = model.models[indexPath.row];
if (subModel.isShow) //是否显示
{
height = subModel.cellHeight;
}
else
{
height = 0;
}
}
else
{
height = model.cellHeight; //地址、药品cell
}
return height;
这里需要注意的是"是否显示cell"这个属性。
现在来到关键的cellForRowAtIndexPath方法。
NSString *cellType =@"";
SubmitOrderBaseModel *data = _orderTypeArray[indexPath.section];
SubmitOrderBaseModel *subData = data.models[indexPath.row];
if (!data.type) //地址、药品Type
{
cellType = subData.type;
}
else
{
cellType = data.type;
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellType];
if (cell ==nil)
{
cell = [[NSClassFromString(cellType) alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellType];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
首先根据cellType来创建cell。然后根据cell的类型(也就是cellType的类型)来为指定的cell赋值。
比如,当前cell是''地址''cell
if ([cell isKindOfClass:[OrderAddressTableViewCell class]])
{
((OrderAddressTableViewCell *)cell).accessoryType = UITableViewCellAccessoryDisclosureIndicator;
((OrderAddressTableViewCell *)cell).nameLabel.text = ((NSDictionary*)subData)[@"RECEIVER"];
((OrderAddressTableViewCell *)cell).phoneLabel.text = ((NSDictionary*)subData)[@"CONTACTPHONE"];
((OrderAddressTableViewCell *)cell).addressLabel.text = ((NSDictionary*)subData)[@"ADDRESS"];
}
这些都是简单的为控件赋值。
在比如,“其他”cell
if ([cell isKindOfClass:[ActionCell class]])
{
if (((SubMitOrderNormalModel*)subData).isShow)
{
((ActionCell *)cell).hidden = NO;
}
else
{
((ActionCell *)cell).hidden = YES;
}
if (((SubMitOrderNormalModel*)subData).isSelected)
{
((ActionCell *)cell).accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
else
{
((ActionCell *)cell).accessoryType = UITableViewCellAccessoryNone;
}
((ActionCell *)cell).titleLab.text = ((SubMitOrderNormalModel*)subData).title; //标题
NSDictionary *infoDic =((SubMitOrderNormalModel*)subData).models[0]; //详情信息
((ActionCell *)cell).actionLab.text = infoDic[@"NAME"];
if (((SubMitOrderNormalModel*)subData).rightColor) //详情字体颜色
{
((ActionCell *)cell).actionLab.textColor= ((SubMitOrderNormalModel*)subData).rightColor;
}
else
{
((ActionCell *)cell).actionLab.textColor= [UIColor blackColor];
}
if ([((SubMitOrderNormalModel*)subData).rightTextAlignment isEqualToString:@"left"]) //详情信息输入方向
{
((ActionCell *)cell).actionLab.textAlignment = NSTextAlignmentLeft;
}
else
{
((ActionCell *)cell).actionLab.textAlignment = NSTextAlignmentRight;
}
到这里可以看出,我们并没有通过 section和row的顺序来为控件赋值,而是通过我们传入的数据来映射到特定的cell。
然后是点击事件,这里我们需要把点击的indexPath,和model传出去进行处理就可以了。
到这里,大家可能会对MVVM有了一定的认识,这种通过只对数据源处理,就可以展示我们界面的内容。极大的增加了代码的灵活性,在以后维护时,我们要想改变cell的展示顺序,或者新增、删除某个cell,我们都可以通过对数据源(即orderTypeArray)的存储顺序,增加、删除某条数据就可以了。