View controllers 通常是 iOS 项目中最大的文件,并且它们包含了许多不必要的代码。所以 View controllers 中的代码几乎总是复用率最低的
把 Data Source 和其他 Protocols 分离出来###
把 UITableViewDataSource 的代码提取出来放到一个单独的类中,是为 view controller 瘦身的强大技术之一。当你多做几次,你就能总结出一些模式,并且创建出可复用的类。
举个栗子,在项目中,类似这种代码我们经常能见到:
在ViewController中使用TableViwe展示数据
#import "ViewController.h"
#import "Number.h"
#define SCREEN_SIZE [UIScreen mainScreen].bounds.size
static NSString * const NumberCellIdentifier = @"NumberCell";
@interface ViewController ()<UITableViewDataSource,UITableViewDelegate>
@property (nonatomic,strong) UITableView * tableView;
@property (nonatomic,strong) NSMutableArray * numbers;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.tableView];
[self getData];
}
- (void)getData{
for (int i = 0; i < 100; i ++) {
Number * number = [[Number alloc] init];
number.number = [NSString stringWithFormat:@"%d",i];
[self.numbers addObject:number];
[self.tableView reloadData];
}
}
#pragma mark - lazy
- (UITableView *)tableView{
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 20, SCREEN_SIZE.width, SCREEN_SIZE.height - 20) style:UITableViewStylePlain];
_tableView.dataSource = self;
_tableView.delegate = self;
[_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:NumberCellIdentifier];
}
return _tableView;
}
- (NSMutableArray *)numbers{
if (!_numbers) {
_numbers = [NSMutableArray array];
}
return _numbers;
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.numbers.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:NumberCellIdentifier forIndexPath:indexPath];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:NumberCellIdentifier];
}
Number * number = self.numbers[indexPath.row];
cell.textLabel.text = number.number;
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 50;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end
Number模型代码只有一个属性,如下:
#import <Foundation/Foundation.h>
@interface Number : NSObject
@property (nonatomic,copy) NSString * number;
@end
此时,我们TableView的DataSource方法是在ViewController中实现的。
这些代码基本都是围绕数组做一些事情,更针对地说,是围绕ViewController所以管理的numbers数组做一些事情。我们可以尝试把数组相关的代码分离到单独的类中,我们使用一个Block来设置Cell,也可以使用delegate来做这件事,这取决于你的习惯。
Block方法分离DataSource###
新建一个ArrayDataSourceOfBlock类,继承NSObject,并且添加以下代码:
ArrayDataSourceOfBlock.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef void (^TableViewCellConfigureBlock)(id cell, id item);
@interface ArrayDataSourceOfBlock : NSObject<UITableViewDataSource>
- (id)initWithItems:(NSArray *)anItems cellIdentifier:(NSString *)aCellIdentifier configureCellBlock:(TableViewCellConfigureBlock)aConfigureCellBlock;
- (id)itemAtIndexPath:(NSIndexPath *)indexPath;
@end
ArrayDataSourceOfBlock.m
#import "ArrayDataSourceOfBlock.h"
@interface ArrayDataSourceOfBlock ()
@property (nonatomic, strong) NSArray * items;
@property (nonatomic, copy) NSString * cellIdentifier;
@property (nonatomic, copy) TableViewCellConfigureBlock configureCellBlock;
@end
@implementation ArrayDataSourceOfBlock
- (instancetype)init{
return nil;
}
- (id)initWithItems:(NSArray *)anItems cellIdentifier:(NSString *)aCellIdentifier configureCellBlock:(TableViewCellConfigureBlock)aConfigureCellBlock{
if (self = [super init]) {
self.items = anItems;
self.cellIdentifier = aCellIdentifier;
self.configureCellBlock = [aConfigureCellBlock copy];
}
return self;
}
- (id)itemAtIndexPath:(NSIndexPath *)indexPath{
return self.items[(NSUInteger)indexPath.row];
}
#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.cellIdentifier forIndexPath:indexPath];
id item = [self itemAtIndexPath:indexPath];
self.configureCellBlock(cell, item);
return cell;
}
@end
回到ViewController,注释掉DataSource代码:
#import "ViewController.h"
#import "Number.h"
#import "ArrayDataSourceOfBlock.h"
#define SCREEN_SIZE [UIScreen mainScreen].bounds.size
static NSString * const NumberCellIdentifier = @"NumberCell";
@interface ViewController ()
@property (nonatomic,strong) UITableView * tableView;
@property (nonatomic,strong) NSMutableArray * numbers;
@property (nonatomic,strong) ArrayDataSourceOfBlock * numbersArrayDataSourceOfBlock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.tableView];
[self getData];
}
- (void)getData{
for (int i = 0; i < 100; i ++) {
Number * number = [[Number alloc] init];
number.number = [NSString stringWithFormat:@"%d",i];
[self.numbers addObject:number];
[self.tableView reloadData];
}
TableViewCellConfigureBlock configureCell = ^(UITableViewCell *cell, Number * number){
cell.textLabel.text = number.number;
};
self.numbersArrayDataSourceOfBlock = [[ArrayDataSourceOfBlock alloc] initWithItems:self.numbers cellIdentifier:NumberCellIdentifier configureCellBlock:configureCell];
self.tableView.dataSource = self.numbersArrayDataSourceOfBlock;
}
#pragma mark - lazy
- (UITableView *)tableView{
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 20, SCREEN_SIZE.width, SCREEN_SIZE.height - 20) style:UITableViewStylePlain];
[_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:NumberCellIdentifier];
}
return _tableView;
}
- (NSMutableArray *)numbers{
if (!_numbers) {
_numbers = [NSMutableArray array];
}
return _numbers;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end
到这里我们使用Block的方法实现了UITableViewDataSource的分离。
Delegate方法分离DataSource###
新建一个ArrarDataSourceOfDelegate类,继承NSObject,并且添加以下代码:
ArrarDataSourceOfDelegate.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@class Number;
@protocol ArrarDataSourceDelegate <NSObject>
- (void)tableViewCellConfigureCell:(UITableViewCell *)cell anItem:(Number *)number;
@end
@interface ArrarDataSourceOfDelegate : NSObject<UITableViewDataSource>
@property (nonatomic,assign) id <ArrarDataSourceDelegate> delegate;
- (id)initWithItems:(NSArray *)anItems cellIdentifier:(NSString *)aCellIdentifier;
- (id)itemAtIndexPath:(NSIndexPath *)indexPath;
@end
ArrarDataSourceOfDelegate.m
#import "ArrarDataSourceOfDelegate.h"
@interface ArrarDataSourceOfDelegate ()
@property (nonatomic,strong) NSArray * items;
@property (nonatomic,copy) NSString * cellIdentifier;
@end
@implementation ArrarDataSourceOfDelegate
- (instancetype)init{
return nil;
}
- (id)initWithItems:(NSArray *)anItems cellIdentifier:(NSString *)aCellIdentifier{
if (self = [super init]) {
self.items = anItems;
self.cellIdentifier = aCellIdentifier;
}
return self;
}
- (id)itemAtIndexPath:(NSIndexPath *)indexPath{
return self.items[(NSUInteger)indexPath.row];
}
#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.cellIdentifier forIndexPath:indexPath];
id item = [self itemAtIndexPath:indexPath];
[self.delegate tableViewCellConfigureCell:cell anItem:item];
return cell;
}
@end
回到ViewController,修改代码如下:
#import "ViewController.h"
#import "Number.h"
#import "ArrarDataSourceOfDelegate.h"
#define SCREEN_SIZE [UIScreen mainScreen].bounds.size
static NSString * const NumberCellIdentifier = @"NumberCell";
@interface ViewController ()<ArrarDataSourceDelegate>
@property (nonatomic,strong) UITableView * tableView;
@property (nonatomic,strong) NSMutableArray * numbers;
@property (nonatomic,strong) ArrarDataSourceOfDelegate * numbersArrayDataSourceOfDelegate;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.tableView];
[self getData];
}
- (void)getData{
for (int i = 0; i < 100; i ++) {
Number * number = [[Number alloc] init];
number.number = [NSString stringWithFormat:@"%d",i];
[self.numbers addObject:number];
[self.tableView reloadData];
}
self.numbersArrayDataSourceOfDelegate = [[ArrarDataSourceOfDelegate alloc] initWithItems:self.numbers cellIdentifier:NumberCellIdentifier];
self.numbersArrayDataSourceOfDelegate.delegate = self;
self.tableView.dataSource = self.numbersArrayDataSourceOfDelegate;
}
#pragma mark - ArrarDataSourceDelegate
- (void)tableViewCellConfigureCell:(UITableViewCell *)cell anItem:(Number *)number{
cell.textLabel.text = number.number;
}
#pragma mark - lazy
- (UITableView *)tableView{
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 20, SCREEN_SIZE.width, SCREEN_SIZE.height - 20) style:UITableViewStylePlain];
[_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:NumberCellIdentifier];
}
return _tableView;
}
- (NSMutableArray *)numbers{
if (!_numbers) {
_numbers = [NSMutableArray array];
}
return _numbers;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end
到这里我们使用Delegate的方法实现了UITableViewDataSource的分离。
看不懂的,对着demo多敲几遍就好了~~