FFEasyModel-简介

2017-04-18  本文已影响60人  炒鸡范

基本介绍

在iOS开发中,我们经常会碰到使用tableview或者collectionview来承接复杂页面展示的情况。面对复杂的页面结构和数据结构,通常会在tableview或者collectionviewdatasourcedelegate中使用大量重复的if..else if..else来描述页面逻辑。当碰到需求变化页面改动时,更是痛苦不堪。

FFEasyModel就是用来解决这种问题的。他的目标是简化delegatedatasource的逻辑代码,将页面逻辑进行统一的管理,使业务逻辑更加清晰,一目了然。

基本使用方法

在PCH中#import "FFEasyModel.h",这将会导入所需要的全部文件,因为我们会在UIView+FFEasyData.h中为UIView扩展一些属性和方法,这是使用FFEasyModel必不可少的.

1.注册需要使用的cell

[self.tableView registerClass:[FFStringTableViewCell class] forCellReuseIdentifier:[FFStringTableViewCell ff_identifier]];

可以看到这里使用到了[FFStringTableViewCell ff_identifier],这是在UIView+FFEasyData.h中已经为我们扩展的方法。

1.创建模型

- (void)updateDataSource
{
    //self.data = @[@"基本collection",@"复杂操作collection"];
    self.dataSource = [NSMutableArray array];
    
    //创建section模型 用来管理整个section相关的信息
    FFEasyTableSectionModel *sectionModel = [[FFEasyTableSectionModel alloc] init];
    
    NSMutableArray *cellModels = [NSMutableArray array];
    for (NSInteger i = 0; i < self.data.count; i ++) {
        //创建cell模型
        //这里将这个cell用到的UI、代理对象、数据对象以及简单点击操作方法传递给cellModel
        __weak typeof(self)weakself = self;
        FFEasyTableCellModel *cellModel = [[FFEasyTableCellModel alloc] initWithClassName:NSStringFromClass([FFStringTableViewCell class]) delegate:self data:self.data[i] tapBlock:^{
            [weakself jumpToPage:i];
        }];
        [cellModels addObject:cellModel];
    }
    
    //把cell模型交给section模型
    sectionModel.cellModels = cellModels;
    //把section模型交给section模型数组
    [self.dataSource addObject:sectionModel];
    
    [self.tableView reloadData];
    
}

3.tableview的delegate和datasource

#pragma mark - tableview delegate & datasource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return self.dataSource.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.dataSource[section].cellModels.count;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return self.dataSource[indexPath.section].cellModels[indexPath.row].cellHeight;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self.dataSource[indexPath.section].cellModels[indexPath.row] cellForTableView:tableView];
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    dispatch_block_t block = self.dataSource[indexPath.section].cellModels[indexPath.row].tapBlock;
    if (block) {
        block();
    }
}

可以看到tableview的delegate和datasource代码已经变得非常简洁,并且可以在多个页面直接复制黏贴使用。

4.cell中实现相关函数

@implementation FFStringTableViewCell

- (void)setFf_data:(NSString *)ff_data
{
    [super setFf_data:ff_data];
    //这里可以做模型赋值,也可以直接调整界面相关元素
    self.textLabel.text = ff_data;
}

- (void)setFf_delegate:(id)ff_delegate
{
    //这里可以做代理对象的赋值
    [super setFf_delegate:ff_delegate];
}

- (void)ff_clear
{
    //这里可以做一些cell复用前的清理工作
}

+ (CGFloat)ff_viewHeightWithModel:(id)model
{
    //返回cell的高度
    return 60.0f;
}


@end

通过以上步骤,整个tableview的展示逻辑都被归在了updateDataSource这个方法里。其他人接手代码时也能够清楚地明白当前页面的展示逻辑。

基本实现原理

1.UIView+FFEasyData.h

@interface UIView (FFEasyData)

@property (nonatomic, strong) id ff_data;//用来存储视图的数据对象
@property (nonatomic, weak) id ff_delegate;//用来存储视图的操作代理对象


- (void)ff_clear;//cell重用时的清除多余资源占用动作

+ (NSString *)ff_identifier;//cell的重用标记

+ (CGFloat)ff_viewHeightWithModel:(id)model;//用来获取tableview情况下的cell高度

+ (CGSize)ff_viewSizeWithModel:(id)model;//用来获取collectionview情况下的cell大小

@end

这里为UIView扩展了相关属性是后面简化代码的关键。

2.FFEasyTableSectionModel.hsection模型

@interface FFEasyTableSectionModel : NSObject

@property (nonatomic, strong) FFEasyTableHeaderModel *headerModel;//头视图模型
@property (nonatomic, copy) NSArray<FFEasyTableCellModel *> *cellModels;//cell模型数组
@property (nonatomic, strong) FFEasyTableFooterModel *footerModel;//脚视图模型

@end

创建一个section模型来保存一个section需要的基本元素,header、footer和cell模型。这个section模型会在tableview的delegate和datasource中为tableview提供相关对象模型。

3.FFEasyTableCellModel.h

@interface FFEasyTableCellModel : NSObject

@property (nonatomic, strong) NSString *className;//保存了视图类名
@property (nonatomic, weak) id delegate;//代理对象
@property (nonatomic, strong) id data;//数据对象
@property (nonatomic, copy) dispatch_block_t tapBlock;//点击操作
@property (nonatomic, assign) CGFloat height;//高度 TODO:做高度缓存

//这个方法来生成cell模型 传入所需要的相关数据
- (instancetype)initWithClassName:(NSString *)className delegate:(id)delegate data:(id)data tapBlock:(dispatch_block_t)tapBlock;

//返回cell视图实例
- (UITableViewCell *)cellForTableView:(UITableView *)tableView;

//获取cell高度
- (CGFloat)cellHeight;

@end

再看.m中的实现

@implementation FFEasyTableCellModel

- (instancetype)initWithClassName:(NSString *)className delegate:(id)delegate data:(id)data tapBlock:(dispatch_block_t)tapBlock
{
    self = [super init];
    if (self) {
        _className = className;
        _delegate = delegate;
        _data = data;
        _tapBlock = tapBlock;
    }
    return self;
}
- (UITableViewCell *)cellForTableView:(UITableView *)tableView
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.className];
    if (!cell) {
        cell = [[NSClassFromString(self.className) alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:self.className];
    }
    cell.ff_delegate = self.delegate;
    cell.ff_data = self.data;
    return cell;
}

- (CGFloat)cellHeight
{
    _height = 0;
    return self.height;
}

- (CGFloat)height
{
    if (!_height) {
        //通过UIView+FFEasyData扩展的ff_viewHeightWithModel向cell获取高度
        _height = [NSClassFromString(self.className) ff_viewHeightWithModel:self.data];
    }
    return _height;
}


@end

这样我们很容易地将原本写在tableview的delegate和datasource中的代码,分别交给了cellModel去实现,赋值设置过程和返回高度交给了cell本身去实现。

更多

原本FFEasyModel只是一个支持tableview的工具,但是在公司业务不断变化中tableview已经无法继续支持复杂的页面逻辑,因此我扩展了对collectionview的相关支持。同时为了保持界面的流畅性和支持对单个section和cell的刷新,我又增加了FFEasyCollectionModel.h来实现相关插入删除更新动作,避免不断updateDataSource造成性能消耗和页面闪动,提升用户体验。

未完成的

collection的header、footer也应该返回Size,我偷了个懒。
代理设置貌似有点问题。
。。。

FFEasyModel DEMO 在这里.

上一篇下一篇

猜你喜欢

热点阅读