为何要轻量级
通常,项目中有些 viewControllers 非常臃肿,把一些控制器不需要知道的代码和逻辑全都放在了控制器的文件中,小则好几百行大则上千,代码风格飘忽不定,前逻辑后数据处理,让擦屁股的人苦不堪言,"尼玛,这都是什么玩意儿!!!".今天我们来研究如何把这些代码搬到合适的位置,让你的 viewControllers 从此告别"脂肪",挺起双峰.
如何变得轻量级
1. 剥去 UITableViewDataSource 代理方法
项目中很多地方会用到 tableView 来展示数据, 同样 viewController 会实现 UITableViewDataSource 的以下代理方法
- (id)itemAtIndexPath:(NSIndexPath *)indexPath{
return _items[indexPath.row];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return _items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
FYBaseCell *cell = (FYBaseCell *)[tableView dequeueReusableCellWithIdentifier:_cellIdentifier];
if (_cellType == kFYCellTypeDefault) {
if (!cell) {
cell = [[DemoCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:_cellIdentifier];
}
}
id item = [self itemAtIndexPath:indexPath];
[cell configureCellContentWithItem:item];
return cell;
}
这些 dataSource 方法可以不用在 viewControllers 中实现,因为 viewControllers 并不关心 cell 如何展示数据,所以我们可以抽象出一个 FYDataSource 类出来,实现 UITableViewDataSource 代理方法
@interface FYDataSource()
@property (nonatomic, copy) NSString *cellIdentifier;
@property (nonatomic, assign) kFYCellType cellType;
@end
- (id)itemAtIndexPath:(NSIndexPath *)indexPath{
return _items[indexPath.row];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return _items.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
FYBaseCell *cell = (FYBaseCell *)[tableView dequeueReusableCellWithIdentifier:_cellIdentifier];
if (_cellType == kFYCellTypeDefault) {
if (!cell) {
cell = [[DemoCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:_cellIdentifier];
}
}
id item = [self itemAtIndexPath:indexPath];
[cell configureCellContentWithItem:item];
return cell;
}
我们再给这个抽象类添加一个实例方法,下面贴出 FYDataSource 这个类的头文件代码
// FYDataSource.h 文件的代码
typedef NS_ENUM(NSInteger, kFYCellType){
kFYCellTypeDefault = 0,
};
@interface FYDataSource : NSObject <UITableViewDataSource>
@property (nonatomic, strong) NSArray *items; //模型数组
/**
* 创建一个FYDataSource对象
*
* @param items 模型数组
* @param cellIdentifier cell的缓存标识符
* @param cellType cell类型
*
* @return 实例好的FYDataSource对象
*/
- (instancetype)initWithItems:(NSArray *)items cellIdentifier:(NSString *)cellIdentifier cellType:(kFYCellType)cellType;
@end
*这样就成功剥离了 UITableViewDataSource 的代理方法,并且多了 FYDataSource 这个工具类, �可以复用, 与此同时你可以实现其它 UITableViewDataSource 代理方法,比如: *
tableView:commitEditingStyle:forRowAtIndexPath:
同样的方法你也可以使用在 UICollectionViewDataSource 这个类上,赶紧给你的 viewControllers 瘦身吧!
*2. 剥去 UITableViewDelegate 方法 *
*同样 UITableViewDelegate 的代理方法在控制器中也占据这不少的篇幅,我们也可以给它剥离出来,这里我把代理方法在 FYDataSource 中,同时通过代理传出一些必要参数给控制器,代码如下: *
// .h 文件
@protocol FYDataSourceDelegate <NSObject>
@optional
/**
* 点击cell的代理方法,传出对应的item模型以及对应的tablview
*
* @param item 对应的item
* @param tableView 对应的tablview
*/
- (void)didSelectedCellWithItem:(id)item tableView:(UITableView *)tableView;
- (CGFloat)heightForHeaderInSection:(NSInteger)section tableView:(UITableView *)tableView;
- (UIView *)viewForHeaderInSection:(NSInteger)section tableView:(UITableView *)tableView;
- (CGFloat)heightForFooterInSection:(NSInteger)section tableView:(UITableView *)tableView;
- (UIView *)viewForFooterInSection:(NSInteger)section tableView:(UITableView *)tableView;
- (NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView;
@end
// .m 文件中的实现
#pragma mark
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
id item = [self itemAtIndexPath:indexPath];
return [_baseCell configureCellHeightWithItem:item];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
id item = [self itemAtIndexPath:indexPath];
FYBaseCell *cell = (FYBaseCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath];
[cell didSelectedWithItem:item];
if (self.delegate && [self.delegate respondsToSelector:@selector(didSelectedCellWithItem:tableView:)]) {
[self.delegate didSelectedCellWithItem:item tableView:tableView];
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
if (self.delegate && [self.delegate respondsToSelector:@selector(heightForHeaderInSection:tableView:)]) {
return [self.delegate heightForFooterInSection:section tableView:tableView];
}
return 1.0f;
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
if (self.delegate && [self.delegate respondsToSelector:@selector(heightForFooterInSection:tableView:)]) {
return [self.delegate heightForFooterInSection:section tableView:tableView];
}
return 1.0f;
}
- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
if (self.delegate && [self.delegate respondsToSelector:@selector(viewForHeaderInSection:tableView:)]) {
return [self.delegate viewForHeaderInSection:section tableView:tableView];
}
return nil;
}
- (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{
if (self.delegate && [self.delegate respondsToSelector:@selector(viewForFooterInSection:tableView:)]) {
return [self.delegate viewForFooterInSection:section tableView:tableView];
}
return nil;
}
* 3. 将某些逻辑移动到 Model 中 *
下面有一个例子,在控制器中给 user 属性赋值一个列表属性:
- (void)loadPriorities {
NSDate* now = [NSDate date];
NSString* formatString = @"startDate <= %@ AND endDate >= %@";
NSPredicate* predicate = [NSPredicate predicateWithFormat:formatString, now, now];
NSSet* priorities = [self.user.priorities filteredSetUsingPredicate:predicate];
self.priorities = [priorities allObjects];
}
如果我们把这些逻辑交给 �User 来处理,控制器就会变得清洁了:
- (void)loadPriorities {
self.priorities = [self.user currentPriorities];
}
逻辑我们通过给 User 创建扩展 User+Extensions.m 来处理:
- (NSArray*)currentPriorities {
NSDate* now = [NSDate date];
NSString* formatString = @"startDate <= %@ AND endDate >= %@";
NSPredicate* predicate = [NSPredicate predicateWithFormat:formatString, now, now];
return [[self.priorities filteredSetUsingPredicate:predicate] allObjects];
}
扩展阅读
Lighter View Controller
Clean Table View Code
总结
给控制器瘦身的方法还有很多,在这里就不一一说了,扩展阅读 介绍了不少,项目中如果用到的话可以节省不少开发时间,另外希望各位多多分享,共同进步.
demo 地址