UITableView 進階使用(1)利用Category等合理複用自定義cell,縮小UIViewController的代碼冗餘

概述

UITableView 做爲Ios開發中最基礎的控件之一,在IOS APP中運用十分普遍。然而不少時候,因爲實際業務的複雜性,在viewController中若是沒有很好地控制UITableView的代碼,則會致使UIViewController的代碼冗餘度太高。json

Demo

在MainViewController中,咱們創建了一個TableView ,並設置了DataSource和delegate爲Self,TableView總共呈現了三行數據,每一行會跳轉一個新的ViewController。ruby

#import "MainViewController.h"
#import "StudentViewController.h"
#import "TeacherAndMasterViewController.h"
#import "CustomTableViewCell.h"

static NSString * CellIdr = @"mainCell";

@implementation MainViewController{
    NSArray * _stuInfo;
    NSArray * _teaInfo;
    NSArray * _masterInfo;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.

    [self initViews];

    [self loadData];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/* #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. } */


#pragma mark - Init

- (void)initViews
{
    [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:CellIdr];
    self.title = @"Main";
}

#pragma mark - Private Logical Methods

- (void)loadData
{
    //實際開發中頗有多是服務端返回的json數據。
    _stuInfo = @[@{kName: @"Naruto", kGrade: @"三年一班", kScore: @(97)}, @{kName: @"Garra", kGrade: @"三年二班", kScore: @(88)}, @{kName: @"Saski", kGrade: @"二年一班", kScore: @(66)}, @{kName: @"sakura", kGrade: @"三年一班", kScore: @(100)}];
    _teaInfo = @[@{kName: @"Kakashi", kGrade: @"三年一班", kLevel: @"上忍"}, @{kName: @"JIRAIYA", kGrade: @"三年二班", kLevel: @"影級"}, @{kName: @"Itachi", kGrade: @"二年一班", kLevel: @"影級"}, @{kName: @"Tsunade", kGrade: @"三年一班", kLevel: @"影級"}];
    _masterInfo = @[@{@"name": @"Hagoromo"}];
}


#pragma mark UITableViewDataSource

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 3;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:CellIdr];
    NSInteger row = indexPath.row;

    if (row == 0) {
        cell.textLabel.text = @"學生信息";
    }
    else if(row == 1)
    {
        cell.textLabel.text = @"教師信息";
    }
    else
    {
        cell.textLabel.text = @"校長信息";
    }

    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

    return cell;
}


#pragma mark - UITableViewDelegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:NO];
    NSInteger row = indexPath.row;

    if (row == 0) {
        StudentViewController * stuVc = [[StudentViewController alloc] init];
        stuVc.stuInfo = _stuInfo;
        [self.navigationController pushViewController:stuVc animated:YES];
    }
    else
    {
        ...
    }
}

@end

MainViewController 模擬器
這裏咱們建立一個TableView來展現三種不一樣對象的信息markdown

咱們準備以學生,老師,校長這種有共性也有差別的對象來舉例,在實際開發中,大部分對象的屬性都有必定的關聯性。
首先說學生對象,咱們設置了名稱,年級,和成績屬性;老師對象與學生對象的區別則是少了成績屬性,多了職稱屬性;校長則只有名稱一個屬性。app

咱們建立一個自定義的TableCell來展現學生信息。ui

#import <UIKit/UIKit.h>

extern NSString * const kName;
extern NSString * const kGrade;
extern NSString * const kScore;
extern NSString * const kLevel;

@interface CustomTableViewCell : UITableViewCell

@property (weak, nonatomic) IBOutlet UILabel *lblFirst;
@property (weak, nonatomic) IBOutlet UILabel *lblSecond;
@property (weak, nonatomic) IBOutlet UILabel *lblThird;
@property (weak, nonatomic) IBOutlet UIImageView *avatar;
#import "CustomTableViewCell.h"

NSString * const kName = @"name";
NSString * const kGrade = @"grade";
NSString * const kScore = @"score";
NSString * const kLevel = @"level";

@implementation CustomTableViewCell

- (void)awakeFromNib {
    // Initialization code
    self.avatar.layer.cornerRadius = self.avatar.layer.frame.size.height/2;
    self.avatar.layer.masksToBounds = YES;
}

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

    // Configure the view for the selected state
}

這裏咱們的學生屬性有三個,咱們能夠在StudentViewController中給TableView的DataSource一一賦值。atom

#import "StudentViewController.h"
#import "CustomTableViewCell.h"

static NSString * stuCellIdr = @"stuCell";

@implementation StudentViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.

    [self initViews];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/* #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. } */


#pragma mark - Init

- (void)initViews
{
    [_tableView registerNib:[UINib nibWithNibName:@"CustomTableViewCell" bundle:nil] forCellReuseIdentifier:stuCellIdr];
    self.title = @"Stu";
}


#pragma mark UITableViewDataSource

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CustomTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:stuCellIdr];
    NSInteger row = indexPath.row;

    NSDictionary * stuInfo = _stuInfo[row];
    cell.avatar.image = [UIImage imageNamed:@"stu"];
    cell.lblFirst.text = stuInfo[kName];
    cell.lblSecond.text = stuInfo[kGrade];
    cell.lblThird.text = [NSString stringWithFormat:@"%@ 分", stuInfo[kScore]];
    cell.lblThird.textColor = [UIColor greenColor];
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

    return cell;
}


#pragma mark - UITableViewDelegate

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 91.f;
}

學生頁面
學生列表spa

代碼這麼寫雖然達到了咱們要的效果,可是實際使用中可能會有一些其餘問題。第一個是若是咱們要展現的對象屬性比較多,再加上其餘的一些業務邏輯,頗有可能就致使UIViewController的代碼很長。另外一個是若是還有其餘的ViewController使用到了這個TableCell,則可能要寫另外一份相同或者類似的代碼了,這就加大了咱們代碼的冗餘度。咱們徹底能夠在Cell裏寫一個方法。code

- (void)setStuContent:(NSDictionary *)stuInfo
{
    self.avatar.image = [UIImage imageNamed:@"stu"];
    self.lblFirst.text = stuInfo[kName];
    self.lblSecond.text = stuInfo[kGrade];
    self.lblThird.text = [NSString stringWithFormat:@"%@ 分", stuInfo[kScore]];
    self.lblThird.textColor = [UIColor greenColor];
}

而後再改掉StudentViewController中DataSource中的代碼。orm

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CustomTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:stuCellIdr];
    NSInteger row = indexPath.row;

    NSDictionary * stuInfo = _stuInfo[row];
    [cell setStuContent:stuInfo];
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

    return cell;
}

這樣 既減小了UIViewController中的代碼,又增長了自定義Cell的複用性。對象

OK,咱們再來跳轉展現教師信息。

教師頁面:
教師頁面

這個時候咱們會發現教師內容和學生內容有很大的類似性,只有最下面的分數Label變成了職稱Label。相比再去爲Teacher定義一個cell,咱們能夠更好地利用已有的CustomTableCell。
這裏咱們新建一個CustomTableCell的Category,在CustomTableCell的(TeacherConfigure)分類中,咱們添加一個專門爲Teacher設置data的方法。

#import "CustomTableViewCell.h"

@interface CustomTableViewCell (TeacherConfigure)

- (void)setTeaInfo:(NSDictionary *)teaInfo;

@end
#import "CustomTableViewCell+TeacherConfigure.h"

@implementation CustomTableViewCell (TeacherConfigure)

- (void)setTeaInfo:(NSDictionary *)teaInfo
{
    self.avatar.image = [UIImage imageNamed:@"tea"];
    self.lblFirst.text = teaInfo[kName];
    self.lblSecond.text = teaInfo[kGrade];
    self.lblThird.text = teaInfo[kLevel];
    self.lblThird.textColor = [UIColor orangeColor];
}

@end

這樣咱們教師Controller的代碼就會是這樣:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CustomTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:teacherCellIdr];
    NSInteger row = indexPath.row;

    NSDictionary * teaInfo = _TeaMasterInfos[row];
    [cell setTeaInfo:teaInfo];
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

    return cell;
}

咱們的MainViewControll裏是將學生,教師,校長分開了;可是在咱們實際開發中頗有可能須要一個UIViewController既能展現教師,又能展現校長。這個時候校長要展現的信息頗有可能又和教師有很大的差異,咱們不得不新建另外一個Cell來展現校長。
咱們固然能夠在同一個ViewController中判斷當前要展現的對象類型,而後在dataSource裏判斷,甚至咱們調用didselect的時候也要進行判斷,例如這樣:

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSInteger row = indexPath.row;

    if(_dataType == DataTeacher)
    {
        CustomTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:teacherCellIdr];
        ...
    }
    else
    {
        MasterTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:_masterCellIdr];
        ...
    }

    return cell;
}


#pragma mark - UITableViewDelegate

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(_dataType == DataTeacher)
    {
        return 91.f;
    }
    else
    {
        return 100.f;
    }
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [tableView deselectRowAtIndexPath:indexPath animated:NO];
    NSInteger row = indexPath.row;

    if(_dataType == DataTeacher)
    {
        ...
    }
    else
    {
        ...
    }
}
@end

校長頁面:
校長頁面

顯然這樣極可能會 使咱們的UIViewController的代碼達到一個很是長的狀態,在咱們維護代碼的時候將會被這些繁瑣的業務邏輯折磨。
一種比較好的解決方法是把TableView的DataSource和Delegate分割出去。例如咱們能夠創建一個MasterDataSource的對面來管理校長的Cell;

#import <UIKit/UIKit.h>

@interface MasterDataSource : NSObject<UITableViewDataSource, UITableViewDelegate>

- (id)initWithMasterInfo:(NSArray *)masterInfo cellIdr:(NSString *)cellIdr;

@end
#import "MasterDataSource.h"
#import "MasterTableViewCell.h"


@implementation MasterDataSource{
    NSArray * _masterInfo;
    NSString * _masterCellIdr;
}

- (id)initWithMasterInfo:(NSArray *)masterInfo cellIdr:(NSString *)cellIdr
{
    if (self = [super init]) {
        _masterInfo = masterInfo;
        _masterCellIdr = cellIdr;
    }

    return self;
}

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

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MasterTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:_masterCellIdr];
    NSInteger row = indexPath.row;

    NSDictionary * masterInfo = _masterInfo[row];
    [cell setMasterContent:masterInfo];
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

    return cell;
}


#pragma mark - UITableViewDelegate

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 117.f;
}

這個時候當這個Controller要展現校長信息的時候能夠:

if (_dataType == DataMaster) {
        [_tableView registerNib:[UINib nibWithNibName:@"MasterTableViewCell" bundle:nil] forCellReuseIdentifier:masterCellIdr];
        self.title = @"Master";
        _masterDs = [[MasterDataSource alloc] initWithMasterInfo:_TeaMasterInfos cellIdr:masterCellIdr];
        _tableView.delegate = _masterDs;
        _tableView.dataSource = _masterDs;
    }

這樣咱們的校長信息有關的代碼就分離出去了,避免了Controller中過多的邏輯判斷,大幅減小了UIViewController的代碼。

相關文章
相關標籤/搜索