1、UIKit中的协议编程
- 在使用过程中,我们知道,通过delegate方法,我们可以处理tableView的一些响应事件,比如点击了cell,通过dataSource,我们设置好tableView的数据源,几行几列,采用的什么样的cell,
UITableView
框架在获取了我们提供好的数据源之后,就可以展示出一个滑动列表出来,并提供了各种API接口来给我们做各种各样的事情,试想一下,如果我们开发过程中,苹果没有提供这样一个UITableView框架,我们是不是得造一个这样简单易用的轮子呢 - 事实上,在现在的各种编码社区中,都存在很多优秀的轮子,造轮子的目的是为了让别人能尽可能少的做一些复杂繁琐的操作,而快速达到某个目的,我们在平时开发的过程中是不是很感谢这些第三方大佬提供的各种优秀库,优秀API
- 回过头来再看看
UITableView
的架构,实际上,这个封装好的UIVIew框架,把所有复杂的整体性的事情都做了,开发者只需要对某一个cell的数据源和事件做相应的处理,特点如下:-
UITableView
只负责整体框架搭建 - 框架与数据源无干扰
- 框架与事件无干扰
-
2、协议编程项目运用
- 举一个iOS开发常遇到的例子,UIViewController里包含一个UITableView,假如涉及5中不同类型的cell,即有五种不能类型的业务要需要处理,每种cell或者说每种业务可能有多个子功能,比如说点击cell的按钮a,点击按钮b,等,业务多起来后,整个controller容器就显的不能看了,具体表现为一下几种现象:
-
cellForRow:
方法,根据不同的业务类型,创建获取cell,赋值cell,众多的if else /switch,写的在一个视图获取函数里 -
didSelected:
,根据不同的业务类型,众多的if else /switch处理不同的业务
-
- 比如我们常说的组件化、模块化,这是一种大的,某个比较完整的功能模块的拆分
- 比如说mvvm,mvp等设计模式,也能达到业务拆分的效果
如何把cellForRow
和didSelected
也从业务中脱离出来呢?
有了这一步的vm想法,但是并不想采用mvvm的结构,vm有点太固化了,比如说相同的类型的cell,如果存在巨大的业务差距,用同一个vm模型去处理就不是那么合理了,那么应该怎样去优化这种结构呢?上面提到的协议就是一个不错的选择。制定好协议方法,让model去遵守协议方法就好了
3、面向协议编程Demo
最终实现效果如下
3.1几个关键因素
- UITableView
- 两种cell
- 5个业务,即5个
viewModel
,如果存在可以复用的vm,可以减少vm个数
3.2demo设计架构图
3.3关键代码
下面通过各个模块的代码来感受下这样设计的好处【由于条件不便加偷懒,代码以图片形式展示】
GLProtocolProgramDemoVC
-
获取数据源
-
UITableView相关代理方法
Protocol
Cells
GLPPDStyle1Cell.h
#import "GLDemoProtol.h"
@interface GLPDDStyle1Cell : UITableViewCell<GLPPDCell1Protocol>
@end
GLPPDStyle1Cell.m
@interface GLPPDStyle1Cell()
@property (nonatomic, strong) UIImageView *iconView;
@property (nonatomic, strong) UILabel *accessLabel;
@property (nonatomic, strong) id<GLPPDStyle1Protocol> model;
@end
- (instancetype)initwithStyle:...
- (void)setupView{
//cell视图搭建
}
- (void)configCellWithModel:(id<GLPPDStyle1Protocol>)model {
self.model = model;
if ([model respondsToSelector:@selector(imageName)]) {
self.iconView.image = [UIImage imageNamed:[model imageName]];
} else {
self.iconView.image = nil;
}
//同理通过代理方法accessTitle获取accessLabel.text
}
*GLPPDStyle2Cell.m
//基本方法和GLPPDStyle1Cell相同
- (void)clickHandle:(UIButton *)sender {
if([self.model respondsToSelector:@selector(didClicked)]){
[self.model didClicked];
}
}
Models
GLPPDStyle1Model1<GLPPDStyle1Protocol>
- (NSString *)identifier {
return kStyleCellID;
}
- (NSString *)imageName {
return self.params[@"icon"] ?: @"tubiaozhizuomoban-- 1";
}
- (NSString *)accessTitle {
return self.params[@"title"]?:@"手机";
}
- (void)didSelected {
DemoBaseVC *target = self.params[@"target"];
if(target){
DemoBaseVC *vc = [[DemoBaseVC alloc] init];
vc.title = [self accessTitle];
[target.navigationController pushController:vc animated:YES];
}
}
-
GLPPDStyle1Model2<GLPPDStyle1Protocol>
同上述model一样的cell样式,但是可能由于业务不同,需要单独建一个vm,看代码
-
GLPPDStyle2Model1<GLPPDStyle2Protocol>
3.4代码分析
- view 、viewModel、vc分离,分工明确
- vc只负责从本地或者服务器获取数据源,生产viewModel,即
createDatas
- UITableView的相关代理方法处理简单,无需关心数据源类型及业务处理
- cell只与视图展示、事件响应有关,不做业务具体操作
- model视图展示数据处理,业务处理
- 各业务model分工明确,可读性,可维护性强
- 新增cell样式,或者新增业务类型,对老业务无影响,只需要新增
cellProtocol
、ModelProtocol
- cell样式相同,可复用已有的cell,业务类型相同,可以复用已有的Model,业务不同或者业务比较复杂,也可以新建Model