UITableView编程之现阶段的问题

做ios开发的同学都知道使用UITableView开发的时候必须实现其dataSource、delegate两个协议,并且需要实现其相应的方法,感觉还算简单。但是随着开发的深入越来越发现几乎每个使用UITableView的页面都需要实现以上2哥协议实现其方法,而且发现每次实现的逻辑大同小异;感觉自己就是代码的搬运工。而且UITableViewCell和UITableView几乎是强绑定关系耦合度太高。由于需要在代理方法中通过indexPath来返回相应的内容因此对以后的业务扩展也不好,哪天产品说这个cell和上面的cell换个位置,这是好可能就需要修改好几处的delegate方法。

以下为传统使用UITableView的代码,真实情况会更加凌乱。

#import "ViewController.h"

@interface ViewController ()<UITableViewDelegate,UITableViewDataSource>

@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (nonatomic,copy) NSArray *data;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];

}

- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
//    根据不同的view获取cell
    UITableViewCell * cell;
    if (indexPath.section == 0) {
        if (indexPath.row == 0) {
            cell = [tableView dequeueReusableCellWithIdentifier:@""];
            cell.textLabel.text = @"";
            cell.imageView.image = [UIImage imageNamed:@""];
        } else if(indexPath.row == 1) {
            cell = [tableView dequeueReusableCellWithIdentifier:@""];
            cell.textLabel.text = @"";
            cell.imageView.image = [UIImage imageNamed:@""];
        } else if (indexPath.row == 0) {
            cell = [tableView dequeueReusableCellWithIdentifier:@""];
            cell.textLabel.text = @"";
            cell.imageView.image = [UIImage imageNamed:@""];
        } else if(indexPath.row == 1) {
            cell = [tableView dequeueReusableCellWithIdentifier:@""];
            cell.textLabel.text = @"";
            cell.imageView.image = [UIImage imageNamed:@""];
        }
    }
    return cell;
}

- (NSInteger)tableView:(nonnull UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.data.count;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 0) {
        if (indexPath.row == 0) {
            return 70;
        } else if(indexPath.row == 1) {
            return 80;
        } else if (indexPath.row == 0) {
            return 90;
        } else if(indexPath.row == 1) {
            return 100;
        }
    }
    return 0;
}

@end

那么我们再想既然以上代码每个UITableView的页面都有并且逻辑差不多,无非就是根据IndexPath或者其他条件作为判断来返回结果。那么根据软件的设计思路,可不可以把和业务逻辑无关的内容封装起来呢?答案当然是肯定的。那么我们就想了,UITableView无非就是需要给Cell提供数据、高度、位置、处理cell点击等内容,而且这些和业务逻辑确实一点关系都没有。那么我们看看兄弟平台的Android是如何处理类似的问题的呢?如果了解Android的同学可能会很熟悉,Android的ListView,GridView等等类似控件都是使用Adapter提供数据给ListView、GridView使用以解决以上ios的问题。那么我们也可以参照Android的设计思路来设计ios开发。因此就开发了一款类似Android平台下的处理方式的框架,并且山寨其命名叫“CHGAdapter”。废话不多说,我们先看一下的使用。

使用CHGAdapter构建UITableView

ViewController中的写法 只需要给UITableView提供数据模型

#import <UIKit/UIKit.h>

@interface Test1ViewController : UIViewController

@end
#import "Test1ViewController.h"
#import "Test1Model.h"

@interface Test1ViewController ()

@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (nonatomic,strong) NSArray *data;

@end

@implementation Test1ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.tableView.cellDatas = @[self.data];
}

///构造cell的数据,此处模拟  这些数据可以从网络获取
-(NSArray*)data {
    if (!_data) {
        _data = @[
                    [Test1Model initWithCellClassName:@"Test1TableViewCell" cellHeight:50 name:@"测试1"],
                    [Test1Model initWithCellClassName:@"Test1TableViewCell" cellHeight:50 name:@"测试2"],
                    [Test1Model initWithCellClassName:@"Test1TableViewCell" cellHeight:50 name:@"测试3"],
                    [Test1Model initWithCellClassName:@"Test1TableViewCell" cellHeight:50 name:@"测试4"]
                  ];
    }
    return _data;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

@end

Model写法 Test1Model类 Model类必须实现CHGTableViewCellModelProtocol,主要返回Model对应的Cell的一些参数,比如当前Model应该使用哪一个Cell类,当前Cell的高度应该是多少。另外还可以根据需求在Model中计算Cell的高度等操作。

#import <Foundation/Foundation.h>
#import "CHGAdapter.h"

@interface Test1Model : NSObject<CHGTableViewCellModelProtocol>

@property (nonatomic,strong) NSString *cellClassName;
@property (nonatomic,assign) CGFloat cellHeight;
//以下为业务参数
@property (nonatomic,strong) NSString *name;
@property (nonatomic,strong) NSString *otherProperty2;

+(instancetype)initWithCellClassName:(NSString*)cellClassName cellHeight:(CGFloat)cellHeight name:(NSString*)name;

@end
#import "Test1Model.h"

@implementation Test1Model


+(instancetype)initWithCellClassName:(NSString*)cellClassName cellHeight:(CGFloat)cellHeight name:(NSString *)name{
    Test1Model * t1 = [Test1Model new];
    t1.cellHeight = cellHeight;
    t1.cellClassName = cellClassName;
    t1.name = name;
    return t1;
}


- (NSString *)cellClassNameInTableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath {
    //如果一个model只对应一个cell  这里可以写死,就不需要外部传入。也可以根据model的某一个字段来判断使用哪一个cell
    return self.cellClassName;
}

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

@end

UITableViewCell的写法 Test1TableViewCell类 必须继承CHGTableViewCell类

#import "CHGTableViewCell.h"

@interface Test1TableViewCell : CHGTableViewCell
@property (weak, nonatomic) IBOutlet UILabel *name;

@end
#import "Test1TableViewCell.h"
#import "Test1Model.h"

@implementation Test1TableViewCell

- (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

- (void)cellForRowAtIndexPath:(NSIndexPath *)indexPath tableView:(UITableView *)tableView withData:(id)data {
    [super cellForRowAtIndexPath:indexPath tableView:tableView withData:data];
    Test1Model * t1 = data;
    self.name.text = t1.name;

}

@end

运行效果图如下

UITableView最简单的用法(CHGAdapter)-LMLPHP

我们发现TableView的上下有一块空白区域,那个空白区域是tableView的HeaderView和FooterView,框架中默认设置HeaderView和FooterView的高度为30,因此我们可以设置其默认高度,代码如下:

#import "Test1ViewController.h"
#import "Test1Model.h"

@interface Test1ViewController ()

@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (nonatomic,strong) NSArray *data;

@end

@implementation Test1ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.tableView.cellDatas = @[self.data];
    //设置默认高度
    self.tableView.tableViewAdapter.headerHeight = 0.01;
    self.tableView.tableViewAdapter.cellHeight = 50;
    self.tableView.tableViewAdapter.footerHeight = 0.01;
}

///构造cell的数据,此处模拟  这些数据可以从网络获取
-(NSArray*)data {
    if (!_data) {
        _data = @[
                    [Test1Model initWithCellClassName:@"Test1TableViewCell" cellHeight:50 name:@"测试1"],
                    [Test1Model initWithCellClassName:@"Test1TableViewCell" cellHeight:50 name:@"测试2"],
                    [Test1Model initWithCellClassName:@"Test1TableViewCell" cellHeight:50 name:@"测试3"],
                    [Test1Model initWithCellClassName:@"Test1TableViewCell" cellHeight:50 name:@"测试4"]
                  ];
    }
    return _data;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

@end

以上代码设置tableView的header、cell、footer的高度:

self.tableView.tableViewAdapter.headerHeight = 0.01;//设置headerView的默认高度如果在model的协议中有设置则优先使用model中返回的

self.tableView.tableViewAdapter.cellHeight = 50;//设置cell的默认高度,如果在model的协议中有设置则优先使用model中返回的

self.tableView.tableViewAdapter.footerHeight = 0.01;//设置footerView的默认高度如果在model的协议中有设置则优先使用model中返回的

设置后运行效果如下:

UITableView最简单的用法(CHGAdapter)-LMLPHP

我们看到只有cell,那么如何添加headerView和footerView呢?请往下看

首先创建header的Model 并且实现CHGTableViewHeaderFooterModelProtocol协议并且实现其required方法

#import <Foundation/Foundation.h>
#import "CHGAdapter.h"

@interface Test1HeaderFooterViewModel : NSObject<CHGTableViewHeaderFooterModelProtocol>

@property (nonatomic,copy) NSString *name;

+(instancetype)initWithName:(NSString*)name;

@end
#import "Test1HeaderFooterViewModel.h"

@implementation Test1HeaderFooterViewModel

+(instancetype)initWithName:(NSString*)name {
    Test1HeaderFooterViewModel * t1hf = [Test1HeaderFooterViewModel new];
    t1hf.name = name;
    return t1hf;
}

- (NSString *)headerFooterClassInTableViw:(UITableView *)tableView section:(NSInteger)section type:(CHGTableViewHeaderFooterViewType)type {
    return @"Test1HeaderFooterView";
}

- (CGFloat)headerFooterHeighInTableViw:(UITableView *)tableView section:(NSInteger)section type:(CHGTableViewHeaderFooterViewType)type {
    return 30;
}

@end

然后添加TableView的headerView  Test1HeaderFooterView类 必须继承CHGTableViewHeaderFooterView类

#import "CHGTableViewHeaderFooterView.h"
#import "CHGAdapter.h"

@interface Test1HeaderFooterView : CHGTableViewHeaderFooterView

@property (nonatomic,weak) IBOutlet UILabel *name;

@end
#import "Test1HeaderFooterView.h"
#import "Test1HeaderFooterViewModel.h"

@implementation Test1HeaderFooterView

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/

- (void)headerFooterForSection:(NSInteger)section inTableView:(UITableView *)tableView withData:(id)data type:(CHGTableViewHeaderFooterViewType)type {
    [super headerFooterForSection:section inTableView:tableView withData:data type:type];
    Test1HeaderFooterViewModel * model = data;
    self.name.text = model.name;
}

@end

这里偷懒使用xib。但是需要注意使用xib来关联headerFooterView必须要做如下设置,否则会失败

UITableView最简单的用法(CHGAdapter)-LMLPHP

然后是ViewController中的设置

#import "Test1ViewController.h"
#import "Test1Model.h"
#import "Test1HeaderFooterViewModel.h"

@interface Test1ViewController ()

@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (nonatomic,strong) NSArray *data;
@property (nonatomic,strong) NSArray *headerData;
@property (nonatomic,strong) NSArray *footerData;

@end

@implementation Test1ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.tableView.cellDatas = @[self.data];
    self.tableView.headerDatas = self.headerData;
    self.tableView.footerDatas = self.footerData;//header和footer的所有元素都可以通用包括model和UI
    //设置默认高度
    self.tableView.tableViewAdapter.headerHeight = 0.01;
    self.tableView.tableViewAdapter.cellHeight = 50;
    self.tableView.tableViewAdapter.footerHeight = 0.01;
}

-(NSArray*)headerData {
    if (!_headerData) {
        _headerData = @[
                        [Test1HeaderFooterViewModel initWithName:@"header 标题"]
                        ];
    }
    return _headerData;
}

-(NSArray*)footerData {
    if (!_footerData) {
        _footerData = @[
                        [Test1HeaderFooterViewModel initWithName:@"footer 标题"]
                        ];
    }
    return _footerData;
}

///构造cell的数据,此处模拟  这些数据可以从网络获取
-(NSArray*)data {
    if (!_data) {
        _data = @[
                    [Test1Model initWithCellClassName:@"Test1TableViewCell" cellHeight:50 name:@"测试1"],
                    [Test1Model initWithCellClassName:@"Test1TableViewCell" cellHeight:50 name:@"测试2"],
                    [Test1Model initWithCellClassName:@"Test1TableViewCell" cellHeight:50 name:@"测试3"],
                    [Test1Model initWithCellClassName:@"Test1TableViewCell" cellHeight:50 name:@"测试4"]
                  ];
    }
    return _data;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

@end

运行效果图

UITableView最简单的用法(CHGAdapter)-LMLPHP

上面demo只有一种cell,接下来我们再创建一种类型的Cell。首先创建一个新model明明Test2Model及cell

Test2Model类

#import <Foundation/Foundation.h>
#import "CHGAdapter.h"

@interface Test2Model : NSObject<CHGTableViewCellModelProtocol>

@property (nonatomic,copy) NSString *placeholder;//输入框占位符
@property (nonatomic,copy) NSString *inputText;//输入的内容

+(instancetype)initWithPlaceholder:(NSString*)placeholder inputText:(NSString*)inputText;

@end
#import "Test2Model.h"

@implementation Test2Model

+(instancetype)initWithPlaceholder:(NSString*)placeholder inputText:(NSString*)inputText {
    Test2Model * t2 = [Test2Model new];
    t2.placeholder = placeholder;
    t2.inputText = inputText;
    return t2;
}

- (NSString *)cellClassNameInTableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath {
    //如果一个model只对应一个cell  这里可以写死,就不需要外部传入。也可以根据model的某一个字段来判断使用哪一个cell
    return @"Test2TableViewCell";
}

- (CGFloat)cellHeighInTableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath {
    return 80;
}

@end

Test2TableViewCell类

#import "CHGTableViewCell.h"

@interface Test2TableViewCell : CHGTableViewCell

@property (nonatomic,weak) IBOutlet UITextField *textField;

@end
#import "Test2TableViewCell.h"
#import "Test2Model.h"

@implementation Test2TableViewCell

- (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

- (void)cellForRowAtIndexPath:(NSIndexPath *)indexPath tableView:(UITableView *)tableView withData:(id)data {
    [super cellForRowAtIndexPath:indexPath tableView:tableView withData:data];
    Test2Model * model = data;
    self.textField.placeholder = model.placeholder;
    self.textField.text = model.inputText;
}

@end

运行效果

UITableView最简单的用法(CHGAdapter)-LMLPHP

以上是运行效果,是不是在想如何把输入框的内容实时的在UIViewController中显示?由于篇幅问题,讲在下一篇文章中详细介绍。

GitHub地址:

OC版Swift版

码云地址:

OC版Swift版

UITableView最简单的用法(CHGAdapter)事件传递

05-04 07:31