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多敲幾遍就好了~~