进阶

含有 tableView 的 ViewController 的瘦

2018-05-25  本文已影响86人  Zsj_Sky

前言

最近在开发 iOS 时,发现有很多的业务展示界面都是使用了 tableView,而且大部分展示的界面都是没有很复杂的业务交互和界面结构。例如下面的2个界面


列表一 列表二

一般来说,正常的开发这上面的2个界面,会写出一下的代码

@interface ViewController1: UIViewController <UITableViewDataSource, UITableViewDelegate>
/* 省略内容*/
@end 

@implementation ViewController1

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.manager getDataSourceCount];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     ViewController1CellConfig  *config = [self.manager configInfoFromIndexPath: indexPath];
      ViewController1Cell * cell = [tableView dequeueReusableCellWithIdentifier:@"ViewController1Cell" forIndexPath:indexPath ];
     [cell configCell: ViewController1CellConfig];
     return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return ViewControllerCellHeight;
}
@end
@interface ViewController2: UIViewController <UITableViewDataSource, UITableViewDelegate>
/* 省略内容*/
@end 

@implementation ViewController2

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.manager getDataSourceCount];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
     ViewController2CellConfig  *config = [self.manager configInfoFromIndexPath: indexPath];
      ViewController2Cell * cell = [tableView dequeueReusableCellWithIdentifier:@"ViewController2Cell" forIndexPath:indexPath ];
     [cell configCell: ViewController2CellConfig];
     return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return ViewControllerCellHeight;
}
@end

一对比,会发现这2个 ViewController 中的代码非常类似,而在我们的业务项目中,远远不止2个类似的代码。既然代码都是如此的类似,那么,我们是否可以将这部分代码抽离出来,抽象出一个公共的,复用的类呢。答案是肯定的。

观察上面代码可以发现, 上述代码,其实都是在重复一个流程, ViewController 从 manager 中获取到数据, 然后在将数据传递到 tableView 和 tableViewCell。 因此,只要我们确定了数据传输的接口,就可以将数据传输这部分代码抽象出来。

定义 Manager 到 ViewController 的接口

从上面代码可知, 从 manager 中获取数据的接口有 getDataSourceCountconfigInfoFromIndexPath 所以,我们可以使用 Protocol 的特性, 定义一个接口文件EPCommonManagerProviderDelegagte,如下:


@protocol EPCommonManagerProviderDelegagte <NSObject>

- (id)cellConfigurationWithIndexPath:(NSIndexPath *)indexPath;

- (NSUInteger)numberOfRowsInSection:(NSInteger)section;

@end

这个协议一共定义了2个内容,负责提供 tableView 行数的 numberOfRowsInSection 和 负责提供 tableViewCell 配置数据的 cellConfigurationWithIndexPath

定义 ViewController 到 tableView 和 Cell 的接口

由于 ViewController 到 tableView 的接口就是 dataSource 的协议内容,这里不多讨论。 下面主要定义了 ViewController 到 Cell 的接口EPCommonCellConfigDelegate

@protocol EPCommonCellConfigDelegate <NSObject>
- (void)cellConfig:(id)cellConfiguration;
@end

抽离出 ViewController 中 tableView 的 dataSource

@interface EPCommonTableViewProvider : NSObject <UITableViewDataSource, UITableViewDelegate>

@property (nonatomic, strong) id<EPCommonCellManagerProviderDelegagte> manager;

@property (nonatomic, strong) NSString *cellIdentifier;

@property (nonatomic, assign) CGFloat cellHeight;

@end


@implementation EPCommonTableViewProvider


- (instancetype)init {
    if (self = [super init]) {
        _cellHeight = 44;
    }
    return self;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (self.manager && [self.manager respondsToSelector:@selector(numberOfRowsInSection:)]) {
        return [self.manager numberOfRowsInSection:section];
    }
    return 0;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (self.cellIdentifier.length == 0) {
        return [[UITableViewCell alloc] init];
    }
    id<EPCommonCellConfigDelegate> cell = [tableView dequeueReusableCellWithIdentifier:self.cellIdentifier forIndexPath:indexPath];
    
    if (self.manager && [self.manager respondsToSelector:@selector(cellConfigurationWithIndexPath:)]) {
        id cellConfiguration = [self.manager cellConfigurationWithIndexPath:indexPath];
        if (cell && [cell respondsToSelector:@selector(cellConfig:)]) {
            [cell cellConfig:cellConfiguration];
        }
    }
    return (UITableViewCell *)cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return self.cellHeight;
}
@end

实际使用

  1. manager 实现 EPCommonManagerProviderDelegagte协议
@interface ViewController1Manager : NSObject <EPCommonManagerProviderDelegagte>
@end
@implementation ViewController1Manager


- ( *)cellConfigurationWithIndexPath:(NSIndexPath *)indexPath {
    return [[ViewControllerCell1Config alloc] initWithModel:self.dataSource[indexPath.row] sortType:self.sortType];
}

- (NSUInteger)numberOfRowsInSection:(NSInteger)section {
    return self.dataSource.count;
}

@end

  1. cell 实现 EPCommonCellConfigDelegate 协议
@interface ViewController1Cell : UITableViewCell <EPCommonCellConfigDelegate>
@end
@implementation ViewController1Cell
- (void)cellConfig:(ViewControllerCell1Config *)config {
  /* 配置 cell */
}
@end
  1. 将 EPCommonTableViewProvider 配置 给 tableView 的 dataSource
@interface ViewController1: UIViewController 
/* 省略内容*/
@property (nonatomic, strong) EPCommonTableViewProvider *provider;
@property (nonatomic, strong) ViewController1Manager *manager;
@property (nonatomic, strong) UITableView *tableView;
@end 

@implementation ViewController1

- (EPCommonTableViewProvider *)provider {
    if (_provider == nil) {
        _provider = [[EPCommonTableViewProvider alloc] init];
        _provider.manager = self.manager;
        _provider.cellHeight = 50;
        _provider.cellIdentifier = @"ViewController1Cell";
    }
    return _provider;
}

- (UITableView *)tableView {
    if (_tableView == nil) {
        _tableView = [[UITableView alloc] init];
        _tableView.dataSource = self.provider;
        [_tableView registerNib:[UINib nibWithNibName:@"ViewController1Cell" bundle:nil] forCellReuseIdentifier:@"ViewController1Cell"];
    }
    return _tableView;
}
@end

对于简单的展示页面,manager 和 cell 的代码几乎没有变化,但是抽离出一个 EPCommonTableViewProvider 可以有效的减少 ViewController 中代码行数,并且也可以减少重复代码的数量。

上一篇下一篇

猜你喜欢

热点阅读