抽取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;
@end

static 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就有这样的例子:

“类别分类”与“区域分类”是使用的同一个封装好的View,但他们的数据源明显来自不同的地方。这时候我们该如何处理呢?

这个时候,我们可以考虑使用protocol。以上面的LFPhotoCell为例:

1. 我们定义一个LFPhotoCellItem协议,并且定义指定的实现方法。

/*一个Cell模型,能接受多种模型数据,那么那些模型数据应该遵守cell定义的协议:LFPhotoCellItem,并且实现的模型中要实现协议中定义的方式*/
@protocol LFPhotoCellItem <NSObject>
- (NSString *)name;
- (NSString *)title;
@end

2. 在LFPhotoCell.h中定义协议属性:

@property (assign, nonatomic) id<LFPhotoCellItem> item;

3. 在LFPhotoCell.m中重写协议属性:

- (void)setItem:(id<LFPhotoCellItem>)item {
    _item = item;
    self.name.text = [item name];
    self.title.text = [item title];
}

4. 每个想传递数据到LFPhotoCell中的模型,必须遵循LFPhotoCellItem协议。

#import "LFPhotoCell.h"

@interface LFPhoto : NSObject <LFPhotoCellItem>

@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) NSString *title;

@end

5. 实现遵守协议的方法

@implementation LFPhoto
// 实现LFPhotoCellItem方法
- (NSString *)name {
    return _name;
}

- (NSString *)title {
    return _title;
}
@end

至此,就完成了协议的自定义工作,现在如果你想更换数据模型的话,只需要将4,5两步的模型更换掉,并且实现协议中定义的方法即可。

最后,在Cell回调中,只需要将传递的具体photo模型更换为item就好了。

void (^cellBlock)(LFPhotoCell *, LFPhoto *) = ^(LFPhotoCell *cell, LFPhoto *photo){
        cell.item = photo;
    };
时间: 2024-10-13 21:30:58

抽取UITableView的DataSource代理方法及同一份View能接受不同模型数据的相关文章

UITableView的常用代理方法

//设置行高 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath{ return 80; } //分区 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Return the number of sections. return 3; } //设置每个区有多少行共有多少行

ios 深刻理解MVC模式—代理方法

在oc中MVC模式得到广泛应用,所谓MVC,即模型Model,视图View,控制器Controller 控制器通过模型数据控制视图,而代理方法则是控制器控制视图的所使用的方法. 使用代理所需要满足的条件: 1.代理协议:里面声明了代理方法 2.视图View定义满足代理协议的代理属性,例如 @property(nonatomic,weak)id<UITableViewDelegate>delegate; 3.给View提供一个或几个方法,方法内View的代理view.delegate 调用它的代

IOS UITableview代理方法总结

tableview的datasource代理 @required的两个数据源方法 1.返回每个 session 中 cell 的个数 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; 2.创建tableviewCell(注意复用) - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtI

UITableView的常用属性和代理方法

以下是近期总结的关于tableView的一些属性和代理方法,以及一些常见的问题,现汇总如下,今后还会持续更新,请继续关注: tableView 的头部和尾部视图属性: UISwitch *footerView = [UISwitch new]; UISwitch *headerView = [UISwitch new]; self.tableView.tableHeaderView = headerView; self.tableView.tableFooterView = footerView

UITableView的全部属性、方法以及代理方法执行顺序,看过之后肯定有收获---董鑫

UITableView-------表视图--继承UIScrollView并遵守NSCoding协议 属性 frame-------------设置控件的位置和大小 backgroundColor--------设置控件的颜色 style--------获取表视图的样式 dataSource---------设置UITableViewDataSource的代理 delegate---------设置UITableViewDelegate代理 sectionHeaderHeight------设置

iOS UITableView代理方法详解

原 iOS UITableView代理方法详解 IOS UITableView的代理方法详解(http://my.oschina.net/u/2340880/blog/404958) 一.补充 在上一篇博客中,http://my.oschina.net/u/2340880/blog/404605,我将IOS中tableView(表视图)的一些常用方法总结了一下,这篇将tableView的代理方法作了总结,对上一篇博客进行了补充. 二.UITableViewDataSourc(数据源代理) 1.必

UITableView 删除行和行排序 这里只介绍代理方法

#pragma mark -代理方法 #pragma mark 设置cell表格高度 -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ return 60; } #pragma mark 当cell实行编辑功能时调用 -(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableVi

UITableView的一些常用设置和代理方法

- (void)viewDidLoad { [super viewDidLoad]; tableview = [[UITableView alloc]initWithFrame:CGRectMake(0, 0,self.view.bounds.size.width,self.view.bounds.size.height)style:UITableViewStyleGrouped]; //    UITableViewStylePlain, //    UITableViewStyleGroup

UItableview全部属性、方法以及代理方法执行顺序

UITableView-------表视图--继承UIScrollView并遵守NSCoding协议 属性 frame-------------设置控件的位置和大小 backgroundColor--------设置控件的颜色 style--------获取表视图的样式 dataSource---------设置UITableViewDataSource的代理 delegate---------设置UITableViewDelegate代理 sectionHeaderHeight------设置