今天简单介绍下为ViewController瘦身的一些想法,不足之处还请指出。
一、关于MVVM设计模式
网上的介绍很多,简单说下我的理解。
个人理解:
MVVM = 控制器 + 视图 + 数据模型 + 视图模型
其中 视图模型View - Model 是将 控制控制器中的 网络请求 下拉刷新 下拉加载 及用户交互操作 剥离出来 放到一个工具类里面 由此达到解耦合为控制器瘦身的目的。
二、关于小型工厂模式的使用
开发过程中UITableView的使用频率很高,可你是怎样创建tableView的呢?UITableViewController是一个不错的选择,自带一个tableView 而且你需要做的只是实现它的协议方法就可以了。省去了创建tableView 签代理的麻烦,而且UITableViewController继承于UIViewController 所以跟你在UIViewController上创建tableView没区别。
此时问题来了,当你的tableView的cell有多种的时候怎么办?肯定是在返回cell的协议方法里面进行判断了。可是这样你不会觉得看着很乱么?而且返回行高的时候也需要判断,这就不可避免增加控制器的代码量,而且返回cell的类型明显不是控制器的责任,控制器只是起协调作用的。这时我们就想可不可以把cell的返回类型 返回行高抽出一个类来实现呢?通过传入一些参数 直接返回对应的行高 cell类型。答案是肯定的。
上面所说ViewModel则可以实现网络请求 用户交互 等业务逻辑 只需传入对应的参数就好。
效果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/e894d4bf1ffd3c7035f49e9338a4b89d.webp?x-image-process=image/format,png)
![](https://i-blog.csdnimg.cn/blog_migrate/88f0aae163b1fc31f9673c8d249b6b2e.webp?x-image-process=image/format,png)
![](https://i-blog.csdnimg.cn/blog_migrate/10ca0e6fb57d4d4835ac64938b5867a0.webp?x-image-process=image/format,png)
// Demo用到的API为新浪新闻 ,一共三种cell。
三、具体实现
实践出真知,通过一个简单的例子感受下。
新建类:
1.网络请求类:JWNetTool,简单对AFN做一个封装
![](https://i-blog.csdnimg.cn/blog_migrate/4179d2a89c90d66ea33d0fb713bdb5e1.webp?x-image-process=image/format,png)
2.数据模型:JWModel
创建一些用得到的属性,封装初始化方法,重写 -(void)setValue:(id)value forUndefinedKey:(NSString *)key
3.ViewModel
![](https://i-blog.csdnimg.cn/blog_migrate/b768243b00f989ef04ee576d6be1991f.webp?x-image-process=image/format,png)
4.cell工厂
![](https://i-blog.csdnimg.cn/blog_migrate/cc36c031e07b6a1d4a90fdd0002450f9.webp?x-image-process=image/format,png)
内部实现
#import "JWCellFactory.h"
#import "JWModel.h"
@implementation JWCellFactory
+ (UITableViewCell *)creatTableViewCellWithModel:(JWModel *)model TableView:(UITableView *)tableView IndexPath:(NSIndexPath *)indexPath
{
return [self creatTableViewCellWithModel:model TableView:tableView IndexPath:indexPath CellType:[self cellTypeWithModel:model]];
}
+ (UITableViewCell *)creatTableViewCellWithModel:(JWModel *)model TableView:(UITableView *)tableView IndexPath:(NSIndexPath *)indexPath CellType:(CellType)type
{
UITableViewCell *cell = nil;
switch (type) {
case 1:
cell = [tableView dequeueReusableCellWithIdentifier:@"JWOnePictureCell"];
tableView.rowHeight = 120;
break;
case 0:
cell = [tableView dequeueReusableCellWithIdentifier:@"JWBigPictureCell"];
cell.backgroundColor = [UIColor yellowColor];
tableView.rowHeight = 150;
break;
case 2:
cell = [tableView dequeueReusableCellWithIdentifier:@"JWThreePicsCell"];
cell.backgroundColor = [UIColor greenColor];
tableView.rowHeight = 180;
break;
default:
break;
}
return cell;
}
+(CellType)cellTypeWithModel:(JWModel *)model
{
if (![model.category isEqualToString:@"hdpic"]) {
if ([model.category isEqualToString:@"cms"]) {
return NewsOneImageCell;
}else{
return NewsOneBigImageCell;
}
}
return NewsThreeImageCell;
}
@end
5.接下来我们看下控制器里的代码:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.models.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
return [JWCellFactory creatTableViewCellWithModel:self.models[indexPath.row] TableView:tableView IndexPath:indexPath];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[JWViewModel cellSeletedActionWithTableView:tableView IndexPath:indexPath ViewController:self Arr:self.models];
}
再看下数据请求相关代码:
self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
[JWViewModel getDataWithUrl:URL TableView:self.tableView ViewController:self];
}];
[self.tableView.mj_header beginRefreshing];
#pragma mark - 下拉加载的实现
self.tableView.mj_footer = [MJRefreshAutoStateFooter footerWithRefreshingBlock:^{
[JWViewModel getMoreDataWithUrl:MORE TableView:self.tableView ViewController:self Models:self.models];
}];
6.看下注册cell方法
[self.tableView registerNib:[UINib nibWithNibName:@"JWOnePictureCell" bundle:nil] forCellReuseIdentifier:@"JWOnePictureCell"];
[self.tableView registerNib:[UINib nibWithNibName:@"JWBigPictureCell" bundle:nil] forCellReuseIdentifier:@"JWBigPictureCell"];
[self.tableView registerNib:[UINib nibWithNibName:@"JWThreePicsCell" bundle:nil] forCellReuseIdentifier:@"JWThreePicsCell"];
看到这里瘦身该做的已经差不多了接下来谈谈tableView一些可以优化的地方
四、UITableView相关优化
1.行高固定用属性赋值:
能不用协议方法就不用协议方法,如果行高确定则通过self.tableView.rowHeight = 100;来赋值
因为协议方法的话会在初始化的时候走很多次 , 假设当前数组有20条数据
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 这个方法会走60次 而且每当一个cell出现的时候都会再走一次这个方法。多次走这个方法主要是动态获取当前tableView的contentSize这一属性。
所以行高确定的话 属性赋值会节省一定的资源
2.行高非固定的话 -> 可以采用缓存机制 将已有行高缓存到字典 下次用的时候可以直接取出 不必再重新计算
3.行高动态变化 但变化幅度不是很大 那么就可以采用估算的方法了.假如行高在120 左右变化 且幅度不是很大 的话 self.tableView.estimatedRowHeight = 120;- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
再结合协议方法缓存行高达到优化作用。
4.刷新方法
insertRowsAtIndexPaths:<#(nonnull NSArray*)#> withRowAnimation:<#(UITableViewRowAnimation)#>
与 reloaData
试想一下 如果我现在已经有1000行cell了,而且都是行高都是动态的那么我再获取20行cell 之后reloaData 那么可以想象了 从第0行一直刷新到第1019行 ,这是很浪费资源的.所以可以采用insert方法去更新cell
Demo 中ViewModel上拉加载方法里有用到:
for (NSDictionary *temp in list) {
JWNewsModel *model = [JWNewsModel modelWithDic:temp];
[arr addObject:model];
vc.dataArr = arr;
[tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForItem:arr.count - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
}
5.异步绘制cell 有兴趣的可以去查一下
6.cell尽量手动创建 尽量少用Xib毕竟 加载时转化成代码也会消耗一定的时间
7.cell尽量不那么复杂 图层尽量少 毕竟cell复杂 绘制的话会消耗更多的时间
8.按需加载cell如果我现在飞快的滑动tableView的话 是不是中间有几个cell没必要立马创建出来 而是首先创建屏幕将要显示的cell?
以上就是我对tableView可以优化 及 控制器瘦身的一些想法 不足之处还请指正,有好的建议的话欢迎提出来O(∩_∩)O哈哈~。