首页 > 代码库 > Factory模式写tableview

Factory模式写tableview

几乎所有的ios app中都会用到uitableview,如果每个tableview都有不同的cell(几乎所有app都会不同),或者说有一个uitableview需要在一个tableview中显示不同的cell,那么逐一写uitableview对于开发者来说是很烦的事情,下面我就用factory模式定义一个uitableviewcontroller来解决重复代码的问题。

1.首先,根据mvc的原理,我们要定义三个父类:model父类,view父类,controller父类。

   BaseCell作为view的父类:

#import <UIKit/UIKit.h>

 

/*---------------------------------------------------------------

 *BaseCell : UITableViewCell

 *自定义cell的基类,我们一般不实例化该类型,而是需要自定义cell时,继承于该类型

 *

 *----------------------------------------------------------------*/

 

@class BaseModel;

@interface BaseCell : UITableViewCell

 

/*-------------------------------------------------------------

 *cell对应的数据模型,子类需要重写该属性的setter方法,实现子类的差异化

 *-----------------------------------------------------------*/

@property (nonatomic, retain) BaseModel *dataForCell;

 

/*------------------------------------------------------------

 *类方法,子类应该重写该方法,用以返回cell单元对应于数据模型的单元高度

 *------------------------------------------------------------*/

+(CGFloat)cellHeightForModel:(BaseModel *)dataForCell;

 

@end

 

 

#import "BaseCell.h"

 

@implementation BaseCell

 

 

 

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier

{

    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];

    if (self)

    {

        // Initialization code

    }

    return self;

}

 

 

- (void)setSelected:(BOOL)selected animated:(BOOL)animated

{

    [super setSelected:selected animated:animated];

 

    // Configure the view for the selected state

}

 

/*------------------------------------------------------------

 *类方法,子类应该重写该方法,用以返回cell单元对应于数据模型的单元高度

 *------------------------------------------------------------*/

+ (CGFloat)cellHeightForModel:(BaseModel *)dataForCell

{

    return 0.0f;

}

 

@end

  //

BaseModel作为model的父类

import <Foundation/Foundation.h>

 

/*---------------------------------------------------------------

 *BaseModel : NSObject

 *自定义数据模型的基类,我们一般不会实例化该类型,而是需要自定义数据模型时,

 *继承于该类型,该类虽然没有添加任何属性和方法,但为面向对象编程机制提供了很多方便

 *比如:用基类的指针指向父类的对象,为我们实现不同数据模型对应于不同的cell类型提供

 *了很大的帮助

 *----------------------------------------------------------------*/

@interface BaseModel : NSObject

 

@end

#import "BaseModel.h"

 

@implementation BaseModel

 

 

 

@end

 

BaseTableViewController作为controller的父类

#import <UIKit/UIKit.h>

/*-------------------------------------------------------------------------------

 *表视图控制器基类:这是一个表视图控制器的基类!!!当需要多个表视图控制器的时候,可以继承该基类,

 *子类只需重写- (NSMutableArray *)hardCode 方法以实现给其tableView封装数据源,

 *无需再写其他相关联的支持方法,提供了代码的极高的通用性

 *--------------------------------------------------------------------------------*/

@interface BaseTableViewController : UIViewController<UITableViewDataSource, UITableViewDelegate>

 

{

    //表示图数据源

    NSMutableArray *_dataSource;

    //表视图

    UITableView    *_tableView;

}

/*-----------------------------------------------------------

 *实现封装数据源需要的数据对象,当服务器还无法提供数据服务时,调用此

 此方法实现临时测试,子类需要重写此方法,来为不同的表视图提供相应的数据源

 *-----------------------------------------------------------*/

- (NSMutableArray *)hardCode;

 

@end

#import "BaseTableViewController.h"

#import "MyCell.h"

#import "CellFactory.h"

 

@interface BaseTableViewController ()

 

@end

 

@implementation BaseTableViewController

 

/*---------------------------------

 *对象在被释放前,释放它所拥有的所有对象

 *--------------------------------*/

 

 

/*-------------------------------------------------------------------------------

 *指派初始化方法,在此方法中初始化和视图显示无关的只加载一次的数据

 *------------------------------------------------------------------------------*/

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil

{

    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

    if (self)

    {

        // Custom initialization

        //数据源初始化

        _dataSource = [self hardCode];

    }

    return self;

}

 

 

/*-------------------------------------------------------------------------------

 *子类需要重写此方法,来实现不同数据对象的封装(即用此方法封装MVC中的Model)

 *------------------------------------------------------------------------------*/

- (NSMutableArray *)hardCode

{

    return nil;

}

 

/*-------------------------------------------------------------------------------

 *self.view加载完成时调用此方法,可见的视图和能重现的数据在此方法中进行设置

 *------------------------------------------------------------------------------*/

- (void)viewDidLoad

{

    [super viewDidLoad];

// Do any additional setup after loading the view.

    

    //1.初始化tableView

    _tableView = [[UITableView alloc] initWithFrame:self.view.bounds];

    //设置控制器为数据源代理

    [_tableView setDataSource:self];

    //设置控制器为tableView行为代理

    [_tableView setDelegate:self];

    //追加tableView到self.view

    [self.view addSubview:_tableView];

    

    /*------------------------------------------------------------------------

     *为项目中会使用到得cell类进行注册,可重用标识符为相应cell的类名,当向tableView发送

     *[tableView dequeueReusableCellWithIdentifier:cellIdentifier]消息时,

     *如果没有可以重用的cell单元,tableView就会用我们注册的类给我们创建一个对应的cell对象

     *------------------------------------------------------------------------*/

    [_tableView registerClass:[MyCell class]  forCellReuseIdentifier:NSStringFromClass([MyCell class])];

    

}

 

#pragma mark - dataSource method

/*-----------------------------

 *返回每段中表格单元的个数

 *----------------------------*/

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

{

    return [_dataSource count];

}

 

/*---------------------------------------------------------------------------------------------------

 *返回指定位置的cell,该方法实现了代码的通用性.首先数据模型是用基类的指针指向子类的对象,然后获得该数据模型对应自定义cell的

 *具体类型,接着用该自定义cell类型获得cell实例,其中cell工厂起到了拆分数据模型和cell强耦合的关系,使得该代码可以完成不同数据

 *模型找到相对应的不同cell的功能,代码的通用性很强,这是面向对象编程的强大之处.

 *--------------------------------------------------------------------------------------------------*/

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{

    //1.取出对应行的数据对象

    BaseModel *modelForCell = [_dataSource objectAtIndex:indexPath.row];

    

    //2.获得该数据模型对应cell的类型

    Class cellClass = [CellFactory cellClassForModel:modelForCell];

    

    //3.以cell对应的类名获取对应的可重用cell单元,如果没有找到可重用cell单元就创建新的cell对象(注意:把获得的对象赋给基类的指针)

    BaseCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([cellClass class])];

    

    //4.为获得cell设置对应的数据

    [cell setDataForCell:modelForCell];

    

    return cell;

}

 

#pragma mark - tableView delegate method

/*---------------------------------------------------------------------

 *>>返回指定位置的cell高度<<i

 *不同类型的cell可能有不同的高度,即使相同类型的cell也可能因为文本内容不同等造成

 *拥有不同的单元高度,我们需要在此方法中找到指定单元所属的cell类型,然后访问cell的

 *返回cell高度的类方法(之所以是类方法,是因为此时cell对象还没有实例化),获取指定cell

 *单元的高度,然后返回给tableView.项目中用到得所有自定义cell子类都需要实现该类方法!

 *---------------------------------------------------------------------*/

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

{

    //1.获得指定单元所对应的数据对象

    BaseModel *model = [_dataSource objectAtIndex:indexPath.row];

    

    //2.获得该数据对象所对应的cell类型

    Class cellClass = [CellFactory cellClassForModel:model];

    

    //3.获得对应的cell类型在显示该数据对象时的高度

    CGFloat height = [cellClass cellHeightForModel:model];

    

    return height;

}

 

- (void)didReceiveMemoryWarning

{

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

 

@end

其次,我们定义一个CellFactory类用来关联model与cell;

#import <Foundation/Foundation.h>

 

/*-----------------------------------------------------------------

 *CellFactory : NSObject(cell工厂)

 *cell工厂,在我们tableView中为不同的cell类型和数据模型提供了关联的方法,它拆分

 *了cell类型和数据模型强耦合的关系,使得我们的代码具有了更强的低耦合高内聚特性,

 *更重要的是,使得我们的代码更有通用性

 *------------------------------------------------------------------*/

@class BaseModel, BaseCell;

@interface CellFactory : NSObject

 

/*-----------------------------------------------------------------

 *类方法,通过给定一个子类化的数据模型和一个重用标识符,创建一个相对应的cell对象.

 *------------------------------------------------------------------*/

+ (BaseCell *)cellForModel:(BaseModel *)modelForCell reuseabledIdentifier:(NSString *)cellIdentifier;

/*-----------------------------------------------------------------

 *类方法,通过给定一个子类化的数据模型,返回该数据模型相对应的cell子类型

 *项目中应该把需要使用到得自定义cell子类型关联到该方法中

 *------------------------------------------------------------------*/

+ (Class)cellClassForModel:(BaseModel *)dataModel;

 

@end

 

#import "CellFactory.h"

#import "BaseCell.h"

#import "BaseModel.h"

/*-----------------------------------

 *引入用到的自定义cell子类型的头文件,以及

 *子类化的数据模型的头文件

 *----------------------------------*/

#import "UserInfo.h"

#import "MyCell.h"

 

@implementation CellFactory

 

+ (BaseCell *)cellForModel:(BaseModel *)modelForCell reuseabledIdentifier:(NSString *)cellIdentifier

{

    //获得数据模型相对应的cell子类

    Class cellClass = [self cellClassForModel:modelForCell];

    

    //用子类对象初始化父类指针

    BaseCell *cell = [[cellClass alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];

    

    return cell ;

}

 

/*-----------------------------------------------------------------

 *类方法,通过给定一个子类化的数据模型,返回该数据模型相对应的cell子类型

 *项目中应该把需要使用到得自定义cell子类型关联到该方法中

 *------------------------------------------------------------------*/

+(Class)cellClassForModel:(BaseModel *)dataModel

{

    Class cellClass = Nil;

    

    if ([dataModel isKindOfClass:[UserInfo class]])

    {

        cellClass = [MyCell class];

    }

 

    return cellClass;

}

@end

 

其三:我们只需要定制好自己的model和cell即可,这里我定义一个model和一个cell,其他依次类推;

 

#import <UIKit/UIKit.h>

#import "BaseCell.h"

 

/*--------------------------------------------------------------------------------------------

 *MyCell : BaseCell (继承于BaseCell的子类)

 *这是一个子类化的自定义cell,在相同的继承于BaseCell的子类中需要重写指派初始化方法完成可见控件的定制工作,

 *以及一些需要的数据的初始化工作.当自定义的可见控件需要lazyLoading时,需要重写相对应属性的getter方法,

 *以完成可见控件的加载!该相同的子类中需要重写父类(dataForCell属性)的setter方法,实现子类定制内容的差异化!

 *该相同关系的子类还应该重写继承于父类的+(CGFloat)cellHeightForModel:(BaseModel *)dataForCell

 *方法,当tableView询问该类,给定一个指定的数据模型,返回一个对应的单元高度值(该单元高度值应该跟随数据内容的多少变化)

 *----------------------------------------------------------------------------------------------*/

@interface MyCell : BaseCell

 

@property (nonatomic, retain) UILabel *nameLabel;

@property (nonatomic, retain) UILabel *phoneLabel;

@property (nonatomic, retain) UIImageView *userImageView;

 

@end

 

#import "MyCell.h"

#import "UserInfo.h"

#import "BaseModel.h"

 

@implementation MyCell

 

/*-------------------------------------------------------------------------------

 *自定义cell的指派初始化方法,在该方法中应该完成自定义cell的初始化工作,

 *如果想实现自定义cell上控件的lazyLoading,则可视化的控件需要在对应属性的getter方法中获得.

 *可视化的控件一定要追加到cell上的contentView上,这样当单元编辑时,可见控制位置会跟着调整.

 *-------------------------------------------------------------------------------*/

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier

{

    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];

    if (self)

    {

        // Initialization code

        self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 5, 200, 30)];

        self.phoneLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 40, 200, 30)] ;

        self.userImageView = [[UIImageView alloc] initWithFrame:CGRectMake(250, 10, 60, 60)];

        

        [self.contentView addSubview:self.nameLabel];

        [self.contentView addSubview:self.phoneLabel];

        [self.contentView addSubview:self.userImageView];

    }

    return self;

}

 

- (void)setSelected:(BOOL)selected animated:(BOOL)animated

{

    [super setSelected:selected animated:animated];

 

    // Configure the view for the selected state

}

 

/*-------------------------------------------------------------

 *重写父类(dataForCell属性)的setter方法,实现子类的差异化

 *-----------------------------------------------------------*/

- (void)setDataForCell:(BaseModel *)dataForCell

{

    //1.调用父类的setter方法,防止递归调用

    [super setDataForCell:dataForCell];

 

    //2.强制转换成相对应的子类化的数据模型

    UserInfo *userInfo = (UserInfo *)dataForCell;

 

    //3.把相对应的数据对象中的数据设置到子类化的cell属性中

    [self.nameLabel     setText:userInfo.name];

    [self.phoneLabel    setText:userInfo.phoneNumber];

    [self.userImageView setImage:userInfo.userImage];

 

}

 

/*------------------------------------------------------------

 *类方法,重写父类的方法,用以返回本类型的cell单元对应于数据模型的单元高度

 *当需要显示的数据内容不同时,应该根据内容调整单元的高度,如:微博应用,应该

 *根据微博文字信息内容的多少决定一个单元表格的高度

 *------------------------------------------------------------*/

+(CGFloat)cellHeightForModel:(BaseModel *)dataForCell

{

    return 80.0f;

}

 

@end

 

 

#import <Foundation/Foundation.h>

#import "BaseModel.h"

 

/*------------------------------------------------

 *UserInfo : BaseModel (继承于BaseModel的子类)

 *这是一个子类化的数据模型,在相同的子类中我们完成我们需要的

 *数据封装,该子类的一个实例对象对应于tableView的一个的cell单元

 *-----------------------------------------------------*/

@interface UserInfo : BaseModel

 

@property (nonatomic, copy)NSString *name;

@property (nonatomic, copy)NSString *phoneNumber;

@property (nonatomic, copy)NSString *imagePath;

 

//指派初始化方法

-(instancetype)initWithName:(NSString *)name

                phoneNumber:(NSString *)phoneNumber

                  imagePath:(NSString *)imagePaht;

//便利构造器

+(instancetype)userInfoWithName:(NSString *)name

                    phoneNumber:(NSString *)phoneNumber

                      imagePath:(NSString *)imagePaht;

//实例方法,返回对象图片

-(UIImage *)userImage;

@end

 

#import "UserInfo.h"

 

@implementation UserInfo

 

/*---------------------------------

 *对象在被释放前,释放它所拥有的所有对象

 *--------------------------------*/

 

 

//指派初始化方法

-(instancetype)initWithName:(NSString *)name

                phoneNumber:(NSString *)phoneNumber

                  imagePath:(NSString *)imagePaht

{

    self = [super init];

    

    if (self)

    {

        [self setName:name];

        [self setPhoneNumber:phoneNumber];

        [self setImagePath:imagePaht];

    }

    return self;

}

 

//便利构造器

+(instancetype)userInfoWithName:(NSString *)name

                    phoneNumber:(NSString *)phoneNumber

                      imagePath:(NSString *)imagePaht

{

    UserInfo *userInfo = [[UserInfo alloc] initWithName:name phoneNumber:phoneNumber imagePath:imagePaht];

    

    return userInfo ;

}

 

//实例方法,返回对象图片

-(UIImage *)userImage

{

    UIImage *image = [UIImage imageWithContentsOfFile:_imagePath];

    return image;

}

@end

 

最后实现我们的tableview很简单,我们只需要在继承BaseTableViewController的子类中放入model数据就可以了,这样是不是工作效率提高了呢,并且也符合mvc开发模式的。

 

#import "BaseTableViewController.h"

 

/*-----------------------------------------------------------

 *这是一个继承于BaseTableViewController的子类,在该类及其相同关系的子类

 *中只需重写封装数据源的方法即可

 *-----------------------------------------------------------*/

@interface HomeTableViewController : BaseTableViewController

 

@end

 

#import "HomeTableViewController.h"

#import "UserInfo.h"

 

@interface HomeTableViewController ()

 

@end

 

@implementation HomeTableViewController

 

//重写父类封装数据源的方法,实现不同表视图控制器的差异化.

-(NSMutableArray *)hardCode

{

    UserInfo *u1 = [UserInfo userInfoWithName:@"robin"    phoneNumber:@"1" imagePath:nil];

    

    UserInfo *u3 = [UserInfo userInfoWithName:@"sogeking" phoneNumber:@"3" imagePath:nil];

    

    UserInfo *u5 = [UserInfo userInfoWithName:@"nami"     phoneNumber:@"5" imagePath:nil];

    

    UserInfo *u7 = [UserInfo userInfoWithName:@"chopper"  phoneNumber:@"7" imagePath:nil];

    

    

    NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:u1,  u3,  u5,  u7,  nil];

    

    int i = 1;

    for (UserInfo *user in array)

    {

        NSString *imageName = [NSString stringWithFormat:@"h%d",i++];

        NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"jpeg"];

        [user setImagePath:imagePath];

    }

    

    return array ;

    

}

 

@end

 

Factory模式写tableview