第四章 UITableView
有两种形式
- UITableViewStylePlain:是正常的表格形式
- UITableViewStyleGrouped:分组形式
如何展示数据
- UITableView需要一个数据源(dataSource)来显示数据
- UITableView会向数据源查询一共有多少行数据以及每一行显示什么数据等
凡是遵守UITableViewDataSource代理都可以成为其数据源
循环利用,这里面并没有循环利用
/**
* 告诉tableView有多少组
*/
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
/**
* 告诉tableView每组有多少行
*/
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
/**
* 告诉tableView的每行显示什么样的cell
*/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell * cell = [[UITableViewCell alloc] init];
JXCarGroup * group = self.groups[indexPath.section];
JXCar * car = group.cars[indexPath.row];
cell.textLabel.text = car.name;
cell.imageView.image = [UIImage imageNamed:car.icon];
return cell;
}
/**
* 告诉tableView的 头部标题
*/
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
/**
* 诉tableView的尾部标题
*/
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
- 循环利用多个cell
/**
* 复用不同的cell
*/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row % 3 == 0) {
static NSString * identifier = @"cell";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
cell.textLabel.text = [NSString stringWithFormat:@"测试数据cell --- %zd",indexPath.row];
return cell;
} else if (indexPath.row % 3 == 1) {
static NSString * identifier = @"cell";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
cell.textLabel.text = [NSString stringWithFormat:@"测试数据cell1 --- %zd",indexPath.row];
return cell;
} else {
static NSString * identifier = @"cell";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
cell.textLabel.text = [NSString stringWithFormat:@"测试数据cell2 --- %zd",indexPath.row];
return cell;
}
}
优化UITableViewCell
优化方法一
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// 0.重用标识
// 被static修饰的局部变量:只会初始化一次,在整个程序中只有一份内存
static NSString * ID = @"cell";
// 1.先去缓存池中寻找可循环利用的cell
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) { // 如果cell为空,缓存池中没有可复用的cell
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
cell.textLabel.text = [NSString stringWithFormat:@"测试数据 -- %zd",indexPath.row];
return cell;
}
优化方法二:提前注册
- (void)viewDidLoad { [super viewDidLoad]; // 注册cell // 这么做的好处就是 在dataSource方法中复用cell的时候,当最开始cell为空的时候直接返回 UITableViewCell [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; } // 这样子就可以直接使用了 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString * identifier = @"cell"; UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier]; JXHero * hero = self.heroes[indexPath.row]; cell.textLabel.text = hero.name; cell.imageView.image = [UIImage imageNamed:hero.icon]; cell.detailTextLabel.text = hero.intro; return cell; }
UITableViewController
- 在UITableViewController中
// 在UITableViewController控制器中
self.view;
self.tableView;所表达的是一个东西
- 在storyboard中设置UITableView的Dynamic Prototypes 的cell,可以设置不同的缓存池中的cell重用标识
- 在代码中利用重标识获取不同的cell
不仅仅是在UITableViewController中才会有cell表格,在ViewController中拖一个tableView,设置Dynamic Prototypes为大于0的数字也回有cell表格
优化方法三:直接在storyboard中设置
- 在storyboard中设置UITableView的Dynamic Prototypes 的cell,可以设置不同的缓存池中的cell重用标识
在代码中利用重标识获取不同的cell
// 这样子就可以直接使用了 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 这里的identifier,设置需要跟storyboard中设置的是一样的。就不需要再次进行注册,可以直接使用 static NSString * identifier = @"cell"; UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier]; JXHero * hero = self.heroes[indexPath.row]; cell.textLabel.text = hero.name; cell.imageView.image = [UIImage imageNamed:hero.icon]; cell.detailTextLabel.text = hero.intro; return cell; }
UITableViewController 设置
//分割线颜色
self.tableView.separatorColor = [UIColor redColor];
// 分割线样式
self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
- cell设置,cell选中样式
cell.selectionStyle = UITableViewCellSelectionStyleNone;
- cell设置,选中颜色
UIView * selectView = [[UIView alloc] init];
selectView.backgroundColor = [UIColor orangeColor];
cell.selectedBackgroundView = selectView;
- cell设置,背景颜色
// 设置背景颜色
cell.backgroundColor = [UIColor redColor];
- cell设置指示器
// 设置指示器
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
// 直接设置图片
cell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"icc"]];
自定义cell
- 等高的cell
- storyboard定义cell
- 1.创建一个继承自的UItableViewCell的子类
- 2.在storyboard中
- 在cell中添加子控件
- 设置cell的重用标识
- 重新设置cell的Class
- 3.在控制器中
- 利用重用标识找到cell
- 给cell传递模型数据
- 4.在自定义的cell中
- 将storyboard中的子控件连线到类扩展中去
- 提供一个模型属性,重写模型属性的set方法,在这个方法中把模型属性设置到子控件上
- storyboard定义cell
xib定义cell
- 1.先创建一个UITableViewCell子类
- 2.创建一个跟cell同名的xib
- 拖拽一个UITableViewCell出来
- 修改cell的Class为我们设置的类
- 设置cell的重用标识
- 往cell中添加控件
3.在控制器中
- 利用registeNib...注册XIB文件
- 如果没有注册xib文件,就需要手动加载xib文件 (NSBundle mainBundle)
- 给cell传递模型数据
4.在自定义cell中
- 将xib中控件连线到类扩展中
- 提供一个模型属性,重写模型属性的set方法,在这个方法中把模型属性设置到子控件上
- 或者将获得cell的xib文件封装起来
源码地址:用xib封装cell方法
- 代码定义cell
非等高的cell
- xib自定义cell
- 1.先创建一个UITableViewCell子类,勾选xib
- 2.设置cell重用标识,添加控件,设置约束
- 3.在cell中封装重用以及传递模型
- xib自定义cell
//
// JXStatusCell.m
// JX非等高cell
//
// Created by yuezuo on 16/4/19.
// Copyright © 2016年 yuezuo. All rights reserved.
//
#import "JXStatusCell.h"
#import "JXStatus.h"
@interface JXStatusCell ()
@property (weak, nonatomic) IBOutlet UIImageView *iconView;
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@property (weak, nonatomic) IBOutlet UIImageView *vipView;
@property (weak, nonatomic) IBOutlet UILabel *contentLabel;
@property (weak, nonatomic) IBOutlet UIImageView *pictureView;
@end
@implementation JXStatusCell
+ (instancetype)cellWithTableView:(UITableView *)tableView {
static NSString * identifier = @"status";
JXStatusCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];
}
return cell;
}
- (void)setStatus:(JXStatus *)status {
_status = status;
if (status.isVip) {
self.nameLabel.textColor = [UIColor orangeColor];
self.vipView.hidden = NO;
} else {
self.nameLabel.textColor = [UIColor blackColor];
self.vipView.hidden = YES;
}
self.iconView.image = [UIImage imageNamed:status.icon];
self.nameLabel.text = status.name;
self.contentLabel.text = status.text;
if (status.picture) {
self.pictureView.hidden = NO;
self.pictureView.image = [UIImage imageNamed:status.picture];
} else {
self.pictureView.hidden = YES;
}
}
- (CGFloat)height {// 返回高度
if (self.pictureView.hidden) { // 如果配图隐藏,说明没有配图
return CGRectGetMaxY(self.contentLabel.frame) + 10;
} else {
return CGRectGetMaxY(self.pictureView.frame) + 10;
}
}
@end
- 4.在控制器中设置代码
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
JXStatusCell * cell = [JXStatusCell cellWithTableView:tableView];
cell.status = self.statusArray[indexPath.row];
// 强制布局 (如果调用的话系统不会显示label等控件,那么高度就不为空)
[cell layoutIfNeeded];
// 存放高度
self.heights[@(indexPath.row)] = @(cell.height);
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [self.heights[@(indexPath.row)] doubleValue];
}
/**
* 性能优化用,返回估计高度
* 只要设置估计高度 estimatedHeightForRowAtIndexPath,会先调用这个方法之后再调用heightForRowAtIndexPath:方法
*/
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 200;
}
当没有设置估计高度方法的时候系统在显示cell的时候回先调用返回高度方法,这样就不能拿到cell高度 非等高cell的代码源码非等高cell源码
- 以上代码我们需要在控制器中实现高度,并调用我们可以改进代码,将高度直接写进模型中的一个属性值,在自定义cell中,调用之后就直接将高度存储
//
// JXStatusCell.m
// JX非等高cell
//
// Created by yuezuo on 16/4/19.
// Copyright © 2016年 yuezuo. All rights reserved.
//
#import "JXStatusCell.h"
#import "JXStatus.h"
@interface JXStatusCell ()
@property (weak, nonatomic) IBOutlet UIImageView *iconView;
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@property (weak, nonatomic) IBOutlet UIImageView *vipView;
@property (weak, nonatomic) IBOutlet UILabel *contentLabel;
@property (weak, nonatomic) IBOutlet UIImageView *pictureView;
@end
@implementation JXStatusCell
+ (instancetype)cellWithTableView:(UITableView *)tableView {
static NSString * identifier = @"status";
JXStatusCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];
}
return cell;
}
// 这个功能主要是当直接约束的时候,label计算可能会有点问题,在显示的时候label下的空白比设置的更多
- (void)awakeFromNib {
// 设置label的每一行的真实宽度
self.contentLabel.preferredMaxLayoutWidth = [UIScreen mainScreen].bounds.size.width - 20 ;
}
- (void)setStatus:(JXStatus *)status {
_status = status;
if (status.isVip) {
self.nameLabel.textColor = [UIColor orangeColor];
self.vipView.hidden = NO;
} else {
self.nameLabel.textColor = [UIColor blackColor];
self.vipView.hidden = YES;
}
self.iconView.image = [UIImage imageNamed:status.icon];
self.nameLabel.text = status.name;
self.contentLabel.text = status.text;
if (status.picture) {
self.pictureView.hidden = NO;
self.pictureView.image = [UIImage imageNamed:status.picture];
} else {
self.pictureView.hidden = YES;
}
// 强制布局
[self layoutIfNeeded];
if (self.pictureView.hidden) { // 如果配图隐藏,说明没有配图
status.cellHeight = CGRectGetMaxY(self.contentLabel.frame) + 10;
} else {
status.cellHeight = CGRectGetMaxY(self.pictureView.frame) + 10;
}
}
@end
在控制器中调用的方法
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
JXStatusCell * cell = [JXStatusCell cellWithTableView:tableView];
cell.status = self.statusArray[indexPath.row];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
JXStatus * cell = self.statusArray[indexPath.row];
return cell.cellHeight;
}
/**
* 性能优化用,返回估计高度
* 只要设置估计高度 estimatedHeightForRowAtIndexPath,会先调用这个方法之后再调用heightForRowAtIndexPath:方法
*/
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 200;
}
优化后代码源码优化后源码
- storyboard自定义cell
- 代码自定义cell
补充
self.tableView.tableHeaderView
设置tableView的表头图片,只需要象征性的设置一个一个宽度,系统会自动设置他的宽度。
self.tableView.tableFooterView
以上两种都会随着tableView的滚动滚动