在IOS开发中用的最为广泛的组件,莫过于UITableView,今天在这篇文章中会详细介绍一下UITableView和UITableViewCell。
什么是UITableView
UITableView有两种形式,一种是分组的,一种是不分组的,下面来看一下样式:
- 分组样式(UITableViewStyleGrouped)

- 不分组样式(UITableViewStylePlain)

UITableView中每行数据都是一个UITableViewCell,在这个控件中为了显示更多的信息,iOS已经在其内部设置好了多个子控件以供开发者使用。
查看源码可知:
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault, // 左侧显示textLabel(不显示detailTextLabel),imageView可选(显示在最左边)
UITableViewCellStyleValue1, // 左侧显示textLabel、右侧显示detailTextLabel(默认蓝色),imageView可选(显示在最左边)
UITableViewCellStyleValue2, // 左侧依次显示textLabel(默认蓝色)和detailTextLabel,imageView可选(显示在最左边)
UITableViewCellStyleSubtitle // 左上方显示textLabel,左下方显示detailTextLabel(默认灰色),imageView可选(显示在最左边)
};
当然,这些子控件并不一定要全部使用,具体操作时可以通过UITableViewCellStyle进行设置。
如何实现UITableView
在实现UITableView之前,我们先建立一个数据模型,让UITableView来进行展示:
@interface Person : NSObject
@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) NSString *sex;
@property (nonatomic,copy) NSString *age;
-(Person *)initWithName:(NSString *)name andSex:(NSString *)sex andAge:(NSString *)age;
@end
实现:
-(Person *)initWithName:(NSString *)name andSex:(NSString *)sex andAge:(NSString *)age{
if(self=[super init]){
self.name=name;
self.age=age;
self.sex=sex;
}
return self;
}
然后在ViewController中添加实现,根据上文中提到过的两种方式,我们进行分别的实现。
UITableViewStylePlain
实现一个UITableView,比如要有一个数据源,如果需要有交互(点击)那么还需要一个代理,即UITableViewDataSource和UITableViewDelegate。
那我们可以都在当前的ViewController中实现:

UITableViewDataSource
对于数据源我们可以有如下实现:
//有几个群组
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
//一个群组有几个item(cell)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return _persons.count;
}
//根据数据源展示界面实现
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
Person *person=_persons[indexPath.row];
UITableViewCell *cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cell.textLabel.text=[person name];
cell.detailTextLabel.text=person.sex;
return cell;
}
UITableViewDelegate
点击实现,我们可以实现如下的方法:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
Person *person=_persons[indexPath.row];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"title" message:person.age delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"ok", nil];
[alert show];
}
初始化UITableView
然后在初始化UITableView即可:
- (void)viewDidLoad {
[super viewDidLoad];
[self initData];
_tableView=[[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain];
_tableView.dataSource=self;
_tableView.delegate = self;
[self.view addSubview:_tableView];
}
-(void)initData{
_persons = [[NSMutableArray alloc]init];
_mans = [[NSMutableArray alloc]init];
_womans = [[NSMutableArray alloc]init];
Person *person1 = [[Person alloc]initWithName:@"jack" andSex:@"man" andAge:@"15"];
Person *person2 = [[Person alloc]initWithName:@"jim" andSex:@"man" andAge:@"15"];
Person *person3 = [[Person alloc]initWithName:@"jj" andSex:@"man" andAge:@"15"];
Person *person4 = [[Person alloc]initWithName:@"jk" andSex:@"man" andAge:@"15"];
Person *person5 = [[Person alloc]initWithName:@"jff" andSex:@"man" andAge:@"15"];
Person *person6 = [[Person alloc]initWithName:@"tom" andSex:@"man" andAge:@"15"];
NSArray *arryman = [NSArray arrayWithObjects:person1,person2,person3,person4,person5,person6,nil];
[self.mans addObjectsFromArray:arryman];
Person *person11 = [[Person alloc]initWithName:@"lily" andSex:@"women" andAge:@"15"];
Person *person21 = [[Person alloc]initWithName:@"lucy" andSex:@"women" andAge:@"15"];
Person *person31 = [[Person alloc]initWithName:@"ll" andSex:@"women" andAge:@"15"];
Person *person41 = [[Person alloc]initWithName:@"lk" andSex:@"women" andAge:@"15"];
Person *person51 = [[Person alloc]initWithName:@"lf" andSex:@"women" andAge:@"15"];
Person *person61 = [[Person alloc]initWithName:@"nancy" andSex:@"women" andAge:@"15"];
NSArray *arrywoman = [NSArray arrayWithObjects:person11,person21,person31,person41,person51,person61,nil];
[self.womans addObjectsFromArray:arrywoman];
[_persons addObjectsFromArray:_mans];
[_persons addObjectsFromArray:_womans];
}
UITableViewStyleGrouped
根据上面的例子我们知道,还是需要实现UITableViewDataSource和UITableViewDelegate。
UITableViewDataSource
在这种形式的UITableViewDataSource,我们需要多实现一些东西:
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 2;//假设我们有两个群组
}
//根据section来区分是哪个群组
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if (section == 0) {
return _mans.count;
}
else {
return _womans.count;
}
}
//实现展示界面的时候也要区分群组
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
Person *person;
if(indexPath.section == 0){
person = _mans[indexPath.row];
}else{
person = _womans[indexPath.row];
}
UITableViewCell *cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cell.textLabel.text=[person name];
cell.detailTextLabel.text=person.sex;
return cell;
}
// 返回每组头标题名称
-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
if (section == 0) {
return @"man";
}else{
return @"woman";
}
}
// 返回每组尾部说明
-(NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section{
if (section == 0) {
return @"man footer";
}else{
return @"woman footer";
}
}
UITableViewDelegate
在UITableViewDelegate的实现中也要区分群组:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
Person *person;
if(indexPath.section == 0){
person = _mans[indexPath.row];
}else{
person = _womans[indexPath.row];
}
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"title" message:person.age delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"ok", nil];
[alert show];
}
初始化UITableView
这里跟上面基本一样,唯一的区别就是设置类型为UITableViewStyleGrouped
_tableView=[[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
更多功能
设置行高
在UITableViewDelegate中还有一些特殊的功能,通过一些方法来实现:
#pragma mark 设置分组标题内容高度
-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
if(section==0){
return 50;
}
return 40;
}
#pragma mark 设置每行高度(每行高度可以不一样)
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 45;
}
#pragma mark 设置尾部说明内容高度
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
return 40;
}
生成索引
在UITableViewDataSource中还有一些特殊的功能,通过一些方法来实现:
#pragma mark 返回每组标题索引
-(NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{
NSLog(@"生成组索引");
NSMutableArray *indexs=[[NSMutableArray alloc]init];
[indexs addObject:@"man"];
[indexs addObject:@"woman"];
return indexs;
}
性能优化
UITableView中的单元格cell是在显示到用户可视区域后创建的,那么如果用户往下滚动就会继续创建显示在屏幕上的单元格,如果用户向上滚动返回到查看过的内容时同样会重新创建之前已经创建过的单元格。如此一来即使UITableView的内容不是太多,如果用户反复的上下滚动,内存也会瞬间飙升,更何况很多时候UITableView的内容是很多的(例如微博展示列表,基本向下滚动是没有底限的)。
做过Android的同学应该知道,在ListView中有一种机制,就是复用view来做优化,IOS的UITableView中其实已经自我实现了这种机制,也就是将当前没有显示的Cell重新显示在将要显示的Cell的位置然后更新其内容。
在UITableView内部有一个缓存池,初始化时使用initWithStyle:(UITableViewCellStyle) reuseIdentifier:(NSString *)方法指定一个可重用标识,就可以将这个cell放到缓存池。然后在使用时使用指定的标识去缓存池中取得对应的cell然后修改cell内容即可。
#pragma mark返回每行的单元格
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
Person *person;
if(indexPath.section == 0){
person = _mans[indexPath.row];
}else{
person = _womans[indexPath.row];
}
//由于此方法调用十分频繁,cell的标示声明成静态变量有利于性能优化
static NSString *cellIdentifier=@"UITableViewCellIdentifierKey1";
//首先根据标识去缓存池取
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(!cell){
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
}
cell.textLabel.text=[person name];
cell.detailTextLabel.text=person.sex;
return cell;
}
UITableViewCell
原生的UITableViewCell
UITableViewCell是构建一个UITableView的基础,在UITableViewCell内部有一个UIView控件作为其他内容的容器,它上面有一个UIImageView和两个UILabel,通过UITableViewCellStyle属性可以对其样式进行控制。
typedef NS_ENUM(NSInteger, UITableViewCellAccessoryType) {
UITableViewCellAccessoryNone, // 不显示任何图标
UITableViewCellAccessoryDisclosureIndicator, // 跳转指示图标
UITableViewCellAccessoryDetailDisclosureButton, // 内容详情图标和跳转指示图标
UITableViewCellAccessoryCheckmark, // 勾选图标
UITableViewCellAccessoryDetailButton NS_ENUM_AVAILABLE_IOS(7_0) // 内容详情图标
};
添加自定义组件
如果上述的属性我都不想用,比如我想用一个UISwitch控件,应该如何进行设置呢?
继续修改一下-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
Person *person;
if(indexPath.section == 0){
person = _mans[indexPath.row];
}else{
person = _womans[indexPath.row];
}
//由于此方法调用十分频繁,cell的标示声明成静态变量有利于性能优化
static NSString *cellIdentifier=@"UITableViewCellIdentifierKey1";
static NSString *cellIdentifierForFirstRow=@"UITableViewCellIdentifierKeyWithSwitch";
//首先根据标识去缓存池取
UITableViewCell *cell;
if (indexPath.row==0) {
cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifierForFirstRow];
}else{
cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
}
if(!cell){
if (indexPath.row==0) {
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifierForFirstRow];
UISwitch *sw=[[UISwitch alloc]init];
[sw addTarget:self action:@selector(switchValueChange:) forControlEvents:UIControlEventValueChanged];
cell.accessoryView=sw;
}else{
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
cell.accessoryType=UITableViewCellAccessoryDetailButton;
}
}
if(indexPath.row==0){
((UISwitch *)cell.accessoryView).tag=indexPath.section;
}
cell.textLabel.text=[person name];
cell.detailTextLabel.text=person.sex;
return cell;
}
#pragma mark 切换开关转化事件
-(void)switchValueChange:(UISwitch *)sw{
NSLog(@"section:%li,switch:%i",(long)sw.tag, sw.on);
}
代码表示我们在第一行添加了一个开关的控件。
效果如图:

自定义UITableViewCell
假如系统提供的UITableViewCell满足不了我们的需要,我们可以进行自定义,新建一个CustomCellTableViewCell继承UITableViewCell
#import <UIKit/UIKit.h>
#import "Person.h"
@interface CustomCellTableViewCell : UITableViewCell
@property (nonatomic,strong) Person *person;
@property (assign,nonatomic) CGFloat height;
@end
实现:
@interface CustomCellTableViewCell(){
UILabel *_text1;
UILabel *_text2;
UILabel *_text3;
}
@end
@implementation CustomCellTableViewCell
- (void)awakeFromNib {
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self initSubView];
}
return self;
}
#pragma mark 初始化视图
-(void)initSubView{
_text1=[[UILabel alloc]init];
_text1.textColor=[UIColor redColor];
[_text1 setFrame:CGRectMake(0, 0, 40, 20)];
[self.contentView addSubview:_text1];
_text2=[[UILabel alloc]init];
_text2.textColor=[UIColor yellowColor];
[_text2 setFrame:CGRectMake(40, 0, 40, 20)];
[self.contentView addSubview:_text2];
_text3=[[UILabel alloc]init];
_text3.textColor=[UIColor blueColor];
[_text3 setFrame:CGRectMake(80, 0, 40, 20)];
[self.contentView addSubview:_text3];
}
#pragma mark 设置
-(void)setPerson:(Person *)person{
[_text1 setText:person.name];
[_text2 setText:person.sex];
[_text3 setText:person.age];
}
@end
然后修改一下使用的ViewController
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellIdentifier=@"UITableViewCellIdentifierKey1";
CustomCellTableViewCell *cell;
cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(!cell){
cell=[[CustomCellTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
Person *person;
if(indexPath.section == 0){
person = _mans[indexPath.row];
}else{
person = _womans[indexPath.row];
}
cell.person=person;
return cell;
}
效果如下图所示:

UITableViewController
很多时候一个UIViewController中只有一个UITableView,因此苹果官方为了方便大家开发直接提供了一个UITableViewController,这个控制器 UITableViewController实现了UITableView数据源和代理协议,内部定义了一个tableView属性供外部访问,同时自动铺满整个屏幕、自动伸缩以方便我们的开发。
实现起来跟前面提到的都一样,所以,这里不再赘述。
总结
UITableView确实给我们提供了一些很强大的功能和展示效果,用好用对UITableView对于IOS程序开发是非常有必要的。
网友评论