抽取UITableView的DataSource代理方法及同一份View能接受不同模

View controllers 通常是 iOS 项目中最大的文件,并且它们包含了许多不必要的代码。所以 View controllers 中的代码几乎总是复用率最低的。比如UITableView常规用法如下:

传统使用方法

1. 定义数据模型

@interface LFPhoto : NSObject@property (nonatomic,copy) NSString *name;@property (nonatomic,copy) NSString *title;@end

2. 设计自定义LFPhotoCell

#import "LFPhoto.h"@interface LFPhotoCell : UITableViewCell@property (weak, nonatomic) IBOutlet UILabel *name;@property (weak, nonatomic) IBOutlet UILabel *title;@property (nonatomic,strong) LFPhoto *photo;+ (instancetype) photoCell;@end@implementation LFPhotoCell+ (instancetype) photoCell {return [[[NSBundle mainBundle] loadNibNamed:@"LFPhotoCell" owner:nil options:nil] lastObject];}- (void)setPhoto:(LFPhoto *)photo {self.name.text = photo.name;self.title.text = photo.title;}@end

3. 利用UITableView展示数据@interface ViewController ()@property (nonatomic,strong) NSArray *photos;@endstatic NSString *const identifer = @"LFPhotoCell";@implementation ViewController#pragma mark – Lazy Load- (NSArray *)photos {if (!_photos) {LFPhoto *p1 = [[LFPhoto alloc] init];p1.name = @"Jason";p1.title = @"OK";LFPhoto *p2 = [[LFPhoto alloc] init];p2.name = @"Rose";p2.title = @"NO";LFPhoto *p3 = [[LFPhoto alloc] init];p3.name = @"Lucy";p3.title = @"Luck";_photos = @[p1,p2,p3];}return _photos;}- (void)viewDidLoad {[super viewDidLoad];}#pragma mark – Data Source- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {return self.photos.count;}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {LFPhotoCell *cell = (LFPhotoCell *)[tableView dequeueReusableCellWithIdentifier:identifer];if(!cell) {cell = [LFPhotoCell photoCell];}cell.photo = self.photos[indexPath.row];return cell;}#pragma mark – Data Delegate- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {return 80;}在书写了N次UITableView的代理方法后,,我们发现Data Source 方法几乎写法不变,改变的只是Cell和model的类型。所以我们可以考虑把UITableViewDataSource的代码提取出来放到一个单独的类中,为View Controller“瘦身”。

抽取UITableView的DataSource代理方法

要抽取数据源方法,我们定义一个遵守UITableViewDataSource协议的类CommonDataSource。这个类初始化的时候,接受模型数据集合及Cell重用的identifer。最终会将模型数据反馈到Cell上面,所以我们可以再定义一个block,用于回调操作。

// 处理cell与model之间的关系typedef void (^cellConfig)(id cell, id model);// 实现UITableViewDataSource协议@interface CommonDataSource : NSObject <UITableViewDataSource>// 自定义数据源- (instancetype) initWithItem:(NSArray *)items cellIdentifer:(NSString *)identifer cellConfigBlock:(cellConfig)cellConfigBlock;@end@interface CommonDataSource()/*数据集合*/@property (nonatomic,strong) NSArray *items;@property (nonatomic,copy) NSString *identifer;@property (nonatomic,copy) cellConfig cellBlock;@end@implementation CommonDataSource- (instancetype) initWithItem:(NSArray *)items cellIdentifer:(NSString *)identifer cellConfigBlock:(cellConfig)cellConfigBlock {if(self = [super init]){self.items = items;self.identifer = identifer;self.cellBlock = [cellConfigBlock copy];}return self;}#pragma mark – 自定义的类中实现 UITableViewDataSource- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {return self.items.count;}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.identifer];// 这里没有判断cell是否为空,因为在 storyboard中设置了 “prototype cells” 为1,并且将对应的 cell的类型设置为LFPhotoCell了。所以肯定有值。然后在cell上面自定义自己想要的控件,与LFPhotoCell类进行连线。id item = self.items[indexPath.row];self.cellBlock(cell,item);return cell;}

代码中dequeueReusableCellWithIdentifer取得的cell一定不为空,因为我在Storyboard中进行了相关属性的设置。

此时,我们就可以在View Controller直接使用自定义的CommonDataSource了,而UITableView的数据源代理方法完全可以取消掉了。

– (void)viewDidLoad {[super viewDidLoad];// 数据源方法可以进行回调工作void (^cellBlock)(LFPhotoCell *, LFPhoto *) = ^(LFPhotoCell *cell, LFPhoto *photo){cell.photo = photo;};// 准备数据源相关数据self.myDataSource = [[CommonDataSource alloc] initWithItem:self.photos cellIdentifer:identifer cellConfigBlock:cellBlock];// 设置数据源// 当自定义的Data Source 赋值给tableView 的 Data Source的时候,就会调用CommonDataSource 中定义的实现 UITableViewDataSource 的数据源方法self.tableView.dataSource = self.myDataSource;}以后使用的时候,我们只需要更改Cell和model的类型就可以了。

同一份View能接受不同模型数据

在实际开发中,有可能有这样的需求:封装了一个View,用来实现某些功能,但有可能数据源有可能不同,美团App就有这样的例子:

别为荒漠的艰难而哭泣,只为奔流入海功成名就那一天,

抽取UITableView的DataSource代理方法及同一份View能接受不同模

相关文章:

你感兴趣的文章:

标签云: