更輕量的 View Controllers

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

簡單的demo,提取碼:tm2p

參考資料:ObjC中國

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容