UIKit框架(21)UITableView实现复杂单元格(一)
上篇文章介绍了UITableView的数据源驱动、重用机制、刷新数据等基本用法
本篇文章介绍如何实现一些复杂的单元格
UITableViewCell单元格对象有四种基本样式,开发中这个基本样式往往不能满足我们的需求,也就是说需要自定义UITableViewCell的样式,介绍主要的两种做法:
1)使用纯代码自定义
2)使用storyboard中的prototype cell
先来介绍一下UITableViewCell
UITableViewCell表格单元格
UITableView中的单元格使用UITableViewCell视图对象
创建方法:
-(instancetype)initWithStyle:(UITableViewCellStyle)stylereuseIdentifier:(NSString*)reuseIdentifier
style参数:cell的预设样式
typedefenum:NSInteger{UITableViewCellStyleDefault,UITableViewCellStyleValue1,UITableViewCellStyleValue2,UITableViewCellStyleSubtitle}UITableViewCellStyle;
reuseIdentifier参数:cell的重用ID
单元格的内部视图:
@property(nonatomic,readonly,retain)UIView*contentView
内部视图中又包含以下三个子视图,这三个子视图在不同样式中显示的个数及样式不同
@property(nonatomic,readonly,retain)UILabel*textLabel@property(nonatomic,readonly,retain)UILabel*detailTextLabel@property(nonatomic,readonly,retain)UIImageView*p_w_picpathView
单元格的其他组件:
@property(nonatomic)UITableViewCellAccessoryTypeaccessoryType//右侧指示视图样式@property(nonatomic,retain)UIView*accessoryView//自定义右侧指示视图
单元格的选择状态:
@property(nonatomic,getter=isSelected)BOOLselected@property(nonatomic)UITableViewCellSelectionStyleselectionStyle
复杂单元格实现(一): 纯代码自定义
实现如下效果:
实现一个游戏人物信息的展示
分析:
UITableView使用典型的MVC设计模式进行开发
a. 模型:游戏英雄信息数据
b. 视图:单元格视图UITableViewCell的子类
c. 控制器:管理模型数组,以及实现tableView的数据源、代理等
1)模型定义
@interfaceAMHeroModel:NSObject@property(nonatomic,copy)NSString*name;//英雄名@property(nonatomic,copy)NSString*title;//英雄称号@property(nonatomic,copy)NSString*icon;//英雄图标@property(nonatomic,copy)NSString*desc;//英雄描述@property(nonatomic,copy)NSString*tags;//英雄标签+(AMHeroModel*)modelWithDict:(NSDictionary*)dict;//构造方法:字典转模型@end
2)控制器管理模型数组并使用懒加载
@interfaceViewController()@property(nonatomic,strong)NSMutableArray*heroArray;//模型数组@property(weak,nonatomic)IBOutletUITableView*tableView;//tableView@end
#pragmamark-heroArray懒加载-(NSMutableArray*)heroArray{if(_heroArray==nil){NSString*plistPath=[[NSBundlemainBundle]pathForResource:@"hero.plist"ofType:nil];NSArray*plistArray=[NSArrayarrayWithContentsOfFile:plistPath];_heroArray=[NSMutableArrayarray];for(NSDictionary*dictinplistArray){AMHeroModel*model=[AMHeroModelmodelWithDict:dict];[_heroArrayaddObject:model];}}return_heroArray;}
3)自定义UITableViewCell子类
需要展示的模型数据包括:4个字符串、一张图片,故UITableViewCell内部应自定义4个UILabel以及一个UIImageView子视图。
实现需要三步:
@interfaceAMHeroCell:UITableViewCell//1.添加一个类方法,获取cell+(AMHeroCell*)cellWithTableView:(UITableView*)tableView;//2.添加模型属性并重写setter方法@property(nonatomic,strong)AMHeroModel*heroModel;//3.提供一个类方法,返回cell的高度+(CGFloat)cellHeight;@end
4)UITableViewCell子类:添加一个类方法获取cell
这一步是自定义UITableViewCell最关键且最复杂的步骤,需要完成:
将cell创建/获取的代码封装、创建所有的子视图、设置所有的子视图的frame
//1.1实现类方法,获取cell+(AMHeroCell*)cellWithTableView:(UITableView*)tableView{AMHeroCell*cell=[tableViewdequeueReusableCellWithIdentifier:@"cell"];if(cell==nil){cell=[[AMHeroCellalloc]initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:@"cell"];}returncell;}
创建所有的子视图操作应当放在initWithStyle方法中,故重写
//1.2重写initWithStyle:方法,添加自定义子视图的代码-(instancetype)initWithStyle:(UITableViewCellStyle)stylereuseIdentifier:(NSString*)reuseIdentifier{if(self=[superinitWithStyle:stylereuseIdentifier:reuseIdentifier]){//1.2.1将没有用的子视图删除[self.p_w_picpathViewremoveFromSuperview];[self.textLabelremoveFromSuperview];[self.detailTextLabelremoveFromSuperview];//1.2.2创建自定义的子视图并进行一次性的属性设置UIImageView*iconImageView=[[UIImageViewalloc]init];self.iconImageView=iconImageView;[self.contentViewaddSubview:self.iconImageView];UILabel*nameLabel=[[UILabelalloc]init];self.nameLabel=nameLabel;[self.contentViewaddSubview:self.nameLabel];self.nameLabel.textColor=[UIColorpurpleColor];self.nameLabel.font=[UIFontsystemFontOfSize:16];UILabel*titleLabel=[[UILabelalloc]init];self.titleLabel=titleLabel;[self.contentViewaddSubview:self.titleLabel];self.titleLabel.textColor=[UIColorgrayColor];self.titleLabel.font=[UIFontsystemFontOfSize:14];UILabel*tagsLabel=[[UILabelalloc]init];self.tagsLabel=tagsLabel;[self.contentViewaddSubview:tagsLabel];self.tagsLabel.textColor=[UIColorredColor];self.tagsLabel.font=[UIFontsystemFontOfSize:12];UILabel*descLabel=[[UILabelalloc]init];self.descLabel=descLabel;[self.contentViewaddSubview:descLabel];self.descLabel.textColor=[UIColorblueColor];self.descLabel.font=[UIFontsystemFontOfSize:12];self.descLabel.numberOfLines=0;}returnself;}
考虑到屏幕适配,使用设定frame的方式且当前为view子类,所以重写layoutSubviews方法
//1.3设置所有子视图的frame或者使用autolayout//如果使用autolayout,代码放在重写的initWithStyle方法中//如果使用frame,放在layoutSubviews方法中-(void)layoutSubviews{[superlayoutSubviews];CGFloatXspace=self.contentView.frame.size.width*0.05;CGFloatYspace=self.contentView.frame.size.height*0.05;CGFloatiX,iY,iW,iH;iX=Xspace;iY=Yspace;iH=self.contentView.frame.size.height*0.9;iW=iH;self.iconImageView.frame=CGRectMake(iX,iY,iW,iH);CGFloatnX,nY,nW,nH;nX=2*Xspace+iW;nY=iY;nW=(self.contentView.frame.size.width-3*Xspace-iW-Xspace)/2;nH=self.contentView.frame.size.height*0.2;self.nameLabel.frame=CGRectMake(nX,nY,nW,nH);CGFloattX,tY,tW,tH;tX=CGRectGetMaxX(self.nameLabel.frame)+Xspace;tY=nY;tW=nW;tH=nH;self.titleLabel.frame=CGRectMake(tX,tY,tW,tH);CGFloattaX,taY,taW,taH;taX=nX;taY=self.contentView.frame.size.height*0.3;taW=self.contentView.frame.size.width-3*Xspace-iW;taH=nH;self.tagsLabel.frame=CGRectMake(taX,taY,taW,taH);CGFloatdX,dY,dW,dH;dX=nX;dY=self.contentView.frame.size.height*0.55;dW=taW;dH=self.contentView.frame.size.height*0.4;self.descLabel.frame=CGRectMake(dX,dY,dW,dH);}
5)UITableViewCell子类:添加模型属性并重写setter方法
在3)中的代码示例中已经看到添加了模型属性
重写setter方法的目的是:对外隐藏子视图,数据显示到子视图的操作封装在setter方法内部
-(void)setHeroModel:(AMHeroModel*)heroModel{_heroModel=heroModel;//图片对象的创建:两种方式://1)p_w_picpathNamed有缓存的图片对象创建方式以空间换时间//self.iconImageView.p_w_picpath=[UIImagep_w_picpathNamed:_heroModel.icon];//2)withContentOfFile方式创建的不使用缓存以时间换空间NSString*path=[[NSBundlemainBundle]pathForResource:_heroModel.iconofType:nil];self.iconImageView.p_w_picpath=[[UIImagealloc]initWithContentsOfFile:path];self.nameLabel.text=_heroModel.name;self.titleLabel.text=_heroModel.title;self.tagsLabel.text=_heroModel.tags;self.descLabel.text=_heroModel.desc;}
6)UITableViewCell子类:类方法返回cell的高度
UITableView中单元格的高度,默认是44,添加一个类方法返回指定的高度,这样做的好处是:当需求有变时,只需要修改UITableViewCell的子类,即高内聚低耦合的编程思想。
+(CGFloat)cellHeight{return140.f;}
7)控制器实现tableView的数据源、代理方法
#pragmamark-tableView的数据源和代理-(NSInteger)numberOfSectionsInTableView:(UITableView*)tableView{return1;}-(NSInteger)tableView:(UITableView*)tableViewnumberOfRowsInSection:(NSInteger)section{returnself.heroArray.count;}-(UITableViewCell*)tableView:(UITableView*)tableViewcellForRowAtIndexPath:(NSIndexPath*)indexPath{return[AMHeroCellcellWithTableView:tableView];}-(void)tableView:(UITableView*)tableViewwillDisplayCell:(UITableViewCell*)cellforRowAtIndexPath:(NSIndexPath*)indexPath{AMHeroCell*heroCell=cell;heroCell.heroModel=self.heroArray[indexPath.row];}-(CGFloat)tableView:(UITableView*)tableViewheightForRowAtIndexPath:(NSIndexPath*)indexPath{return[AMHeroCellcellHeight];}
可以看到数据源、代理方法的实现极其简单,且无论模型数据怎么变,cell怎么变,这部分的代码是几乎不变的
案例的额外功能扩展
1)添加功能
从截图可以看到,导航栏有一个加号按钮,实现响应方法:随机添加一个英雄
#pragmamark-添加cell-(void)addBarBtnClicked{NSIntegerindex=random()%self.heroArray.count;AMHeroModel*model=self.heroArray[index];[self.heroArrayinsertObject:modelatIndex:0];[self.tableViewreloadData];}
2)删除功能
从截图可以看到,导航栏有一个删除按钮,点击后UITableView进入编辑状态
#pragmamark-删除cell-(void)delBarBtnClicked{[self.tableViewsetEditing:!self.tableView.isEditinganimated:YES];}
点击编辑状态的删除按钮的响应方法
-(void)tableView:(UITableView*)tableViewcommitEditingStyle:(UITableViewCellEditingStyle)editingStyleforRowAtIndexPath:(NSIndexPath*)indexPath{//当进入编辑状态后,点击delete按钮时按钮[self.heroArrayremoveObjectAtIndex:indexPath.row];[self.tableViewreloadData];}
3)点击一个cell,弹出一个UIAlertController,可以进行简单的数据修改
-(void)tableView:(UITableView*)tableViewdidSelectRowAtIndexPath:(NSIndexPath*)indexPath{UIAlertController*ac=[UIAlertControlleralertControllerWithTitle:@"修改英雄信息"message:@"输入修改该的数据"preferredStyle:UIAlertControllerStyleAlert];UIAlertAction*a1=[UIAlertActionactionWithTitle:@"取消"style:UIAlertActionStyleCancelhandler:^(UIAlertAction*_Nonnullaction){}];UIAlertAction*a2=[UIAlertActionactionWithTitle:@"确定"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction*_Nonnullaction){model.name=ac.textFields[0].text;model.title=ac.textFields[1].text;[self.tableViewreloadData];//重新加载数据}];[acaddAction:a1];[acaddAction:a2];[acaddTextFieldWithConfigurationHandler:^(UITextField*_NonnulltextField){textField.text=model.name;}];[acaddTextFieldWithConfigurationHandler:^(UITextField*_NonnulltextField){textField.text=model.title;}];[selfpresentViewController:acanimated:YEScompletion:nil];}
效果:
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。