首页 > 代码库 > 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