iOS开发之让控制器变得更轻量级

为何要轻量级

通常,项目中有些 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 
@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 

@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 *)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 地址

你可能感兴趣的:(iOS开发之让控制器变得更轻量级)