前言
在iOS开发中,UITableView
和 UICollectionView
是常用的列表视图控件,而 Cell复用 和 自定义Cell 是优化其性能和灵活性的关键技术。
Cell的复用
当 UITableView
或 UICollectionView
显示大量数据时,系统不会为每个数据项都创建一个新的 Cell
,系统会维护一个可重用的Cell
队列,当Cell
滚出屏幕时会被放入队列,而当需要显示新的Cell时,系统会优先从队列中取出可重用的Cell进行配置,即 复用已经滚出屏幕的Cell,以减少内存占用和提升性能。
实现Cell的复用有两种方法:
- 非注册方法(即手动判空)
- 注册方法(即使用Cell的自动注册机制)
非注册方法(手动)
手动复用Cell的方法步骤:
- 注册Cell类(即在设置复用标识符):创建一个局部字符串变量作为Cell的类型。在创建Cell时,将这个标识符作为参数传入
- 复用Cell:通过调用
UITableView
或UICollectionView
的dequeueReusableCell(withIdentifier:)方法来请求一个已经不显示在屏幕但是没有被销毁的Cell来显示新的Cell - 配置Cell:重新配置复用的Cell以显示新的数据
代码演示:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *strID = @"id";
UITableViewCell *cell = [_tabView dequeueReusableCellWithIdentifier: strID];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier: strID];
}
cell.textLabel.text = @"aaa";
return cell;
}
注册方法(自动)
与手动复用Cell的区别在于不用手动判空。
自动复用Cell的方法步骤:
- 注册Cell类型:创建一个局部字符串变量作为Cell的类型。在创建Cell时,将这个标识符作为参数传入
- 请求复用的Cell:使用dequeueReusableCell(WithIdentifier:)获取可复用的cell
- 创建新的Cell:不用判空,如果dequeueReusableCell(WithIdentifier:)方法返回nil,自动创建一个新的Cell并返回
- 配置Cell:重新配置复用的Cell以显示新的数据
代码演示:
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *strID = @"id";
MyTableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier: strID];
cell.backgroundColor = [UIColor redColor];
return cell;
}
自定义Cell
默认的 UITableViewCell
或 UICollectionViewCell
样式有限,通常需要 自定义Cell 来满足UI需求。
自定义Cell的方法步骤:
- 初始化阶段:
- ViewController加载时创建并配置UITableView
- 注册自定义Cell类与复用标识的关联
- Cell创建阶段:
- 当tableView需要显示cell时,调用
cellForRowAtIndexPath:
- 系统首先尝试从复用队列中获取cell
- 如果没有可复用的cell,系统会调用
initWithStyle:reuseIdentifier:
初始化新的cell - 在初始化方法中创建并配置label1和label2
- 当tableView需要显示cell时,调用
- 布局阶段:
- 当cell需要显示时,系统调用
layoutSubviews
- 在layoutSubviews中设置两个label的精确位置
- 当cell需要显示时,系统调用
- 复用阶段:
- 当cell滚出屏幕时,会被放入复用队列
- 当新的cell需要显示时,优先从复用队列中获取
代码演示:
UICellTableViewCell.h
定义一个继承UITableViewCell的Cell类
#import <UIKit/UIKit.h>
@interface UICellTableViewCell : UITableViewCell
@property(nonatomic, strong) UILabel *label1;
@property(nonatomic, strong) UILabel *label2;
@end
UICellTableViewCell.m
初始化方法和布局方法
#import "UICellTableViewCell.h"
@implementation UICellTableViewCell
-(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if ([reuseIdentifier isEqual:@"cell"]) {
_label1 = [[UILabel alloc] init];
_label1.textColor = [UIColor greenColor];
_label1.font = [UIFont systemFontOfSize:20];
[self.contentView addSubview:_label1];
_label2 = [[UILabel alloc] init];
_label2.textColor = [UIColor cyanColor];
_label2.font = [UIFont systemFontOfSize:15];
[self.contentView addSubview:_label2];
}
return self;
}
-(void)layoutSubviews {
_label1.frame = CGRectMake(100, 20, self.contentView.bounds.size.width - 40, 20);
_label2.frame = CGRectMake(100, 40, self.contentView.bounds.size.width - 40, 20);
}
注册自定义Cell类,将tableView添加到视图
//ViewController.h
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
UITableView *_tableView;
}
@end
//ViewController.m
#import "ViewController.h"
#import "UICellTableViewCell.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];
_tableView.delegate = self;
_tableView.dataSource = self;
[_tableView registerClass:[UICellTableViewCell class] forCellReuseIdentifier:@"cell"];
[self.view addSubview:_tableView];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 15;;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UICellTableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:@"cell"];
cell.label1.text = @"一级标题";
cell.label2.text = @"二级标题";
return cell;
}
@end
注意:所有子视图都应该添加到contentView上,而不是直接添加到cell本身。