cell的复用机制和自定义cell
UITableView
在学习cell之前,我们需要先了解UITableView
。UITableView
继承于UIScrollView
,拥有两个两个相关协议
UITableViewDelegate和UITableViewDataSource,前者用于显示单元格,设置行高以及对单元格进行指定操作,插入头视图和脚视图,后者用于设置TableView
的section和row的数量(section相当于行,row相当于列)。
cell的复用方式
非注册
使用非注册方法,对cell类进行注册,我们需要对cell进行判空
- 非注册方式是直接通过实例化单元格对象,并在需要时手动创建和配置每个单元格。
- 这种方式通常在简单的表格或特殊情况下使用,不需要频繁的单元格重用。
- 使用非注册方式时,可以通过实例方法
UITableViewCell(style:reuseIdentifier:)
或其他自定义初始化方法来创建单元格对象。 - 每次需要显示新的单元格时,都会实时创建新的单元格对象,而不会尝试重用已存在的单元格。
- 非注册方式的优点是简单直接,适用于一些简单的表格或特殊的使用情况。
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = @"mycell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
// Configure the cell...
...
return cell;
}
注册
注册单元格是通过调用 UITableView 的
register(_:forCellReuseIdentifier:)
方法来完成的。通常在viewDidLoad
或类似的初始化方法中执行。在注册单元格时,您需要提供一个标识符(ReuseIdentifier),用于标识特定类型的单元格。
当需要创建新的单元格时,UITableView 会使用注册的标识符来实例化单元格对象,并自动管理单元格的重用。
每次调用
dequeueReusableCell(withIdentifier:for:)
方法获取单元格时,UITableView 都会尝试从重用池中获取已注册的单元格,如果池中没有可重用的单元格,则根据注册信息创建新的单元格。注册单元格的好处是可以提高性能,因为它使 UITableView 能够有效地管理单元格的重用和内存占用,从而避免不必要的创建和销毁。
- (void)viewDidLoad
{
[super viewDidLoad];
// 如果使用代码自定义 Cell
[self.tableView registerClass:[CustomCell class] forCellReuseIdentifier:@"myCell"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = @"mycell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
// Configure the cell...
...
return cell;
}
区别
- 使用注册方法在程序的实现之中不需要进行判空操作,是因为已经在初始化阶段对需要使用的cell类进行注册,所以就可以免去判空的操作
- 我们可以注意到在获取 Cell 时,两种方式调用了不同的 method,
dequeueReusableCellWithIdentifier:identifier
第一个 method 用在了非注册的方式里,equeueReusableCellWithIdentifier:identifier forIndexPath:indexPath
第二个 method 用在了需要注册的方式里。
cell的复用原理
cell的复用原理是使用三个容器进行实现
- Cell的缓存字典和Section的缓存Array:
- 为了提高复用性能,可以使用缓存字典和缓存数组来存储已创建的 UITableViewCell 实例。
- 缓存字典的键是重用标识符,值是一个数组,每个元素都是具有相同重用标识符的可复用单元格实例。
- 缓存数组用于缓存每个 section 中的单元格数据,使得访问和管理每个 section 的单元格更加方便。
- 可复用集合(Mutable Set):
- 可复用集合是一个可变的集合(如 NSMutableSet),用于存放当前可复用的 UITableViewCell 实例。
- 当单元格滚动离开屏幕时,它会被添加到可复用集合中,以备后续的复用。
- 当需要获取可复用的单元格时,首先从可复用集合中检查是否有可用的单元格实例。
这个可复用的集合其实就是我们所说的复用池,也称之为_reusableCells:
关于cell的复用
单元格在显示的时候就会创建视图中可看到的单元格数+ 1的单元格。在UITableView
滚动的过程中,会使用复用机制进行对单元格对象的管理,避免了频繁创建和销毁单元格,以达到提高性能和内存的利用率。当某个单元格离开屏幕范围时,它会被回收并放入_reusableCells
集合中,等待被重复使用。当新的单元格需要显示时,UITableView会首先尝试从_reusableCells
中获取一个可复用的单元格对象,如果_reusableCells
中没有可用的单元格,则会通过实例化新的UITableViewCell对象来满足需求。
即通俗的来说,当滑动的等操作使原本在屏幕上的cell不显示在屏幕上,就会将移除到单元格中的复用池之中,然后再加载新的cell的时候也并不是新创建一个cell,而是直接从对象池中取出一个cell对象,然后给它的相关属性赋上新的值,从而实现cell的复用。
自定义cell
由于系统给出的cell只能够实现文字,所有时候我们就需要使用自定cell,来生成我们想要的单元格格式
自定义cell的实现需要以下步骤
- 创建 UITableViewCell 的子类
- 定义 UITableViewCell 的界面和布局
- 注册和使用自定义 UITableViewCell
步骤一:
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface JCTableViewCell : UITableViewCell
@property (nonatomic, strong) UILabel *label;
@property (nonatomic, strong) UIButton *button;
@end
NS_ASSUME_NONNULL_END
步骤二:
#import "JCTableViewCell.h"
@implementation JCTableViewCell
- (void)awakeFromNib {
[super 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];
self.label = [[UILabel alloc] init];
self.label.text = @"子视图";
self.label.backgroundColor = [UIColor redColor];
[self.contentView addSubview:_label];
self.button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[self.button setTitle:@"按钮" forState:UIControlStateNormal];
[self.button addTarget:self action:@selector(buttonTapped) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:self.button];
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.label.frame = CGRectMake(0, 0, 70, 80);
self.button.frame = CGRectMake(100, 0, 70, 70);
}
- (void)buttonTapped {
// 在此处理按钮点击事件
NSLog(@"按钮被点击");
}
@end
步骤三:
#import "ViewController.h"
#import "JCTableViewCell.h"
@interface ViewController ()
@end
static NSString *str = @"id";
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
tview = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
tview.delegate = self;
tview.dataSource = self;
tview.backgroundColor = [UIColor grayColor];
[tview registerClass:[JCTableViewCell class] forCellReuseIdentifier:str];
[self.view addSubview:tview];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 80.0; // 设置为适当的单元格高度
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
JCTableViewCell *cell = [tview dequeueReusableCellWithIdentifier:str];
return cell;
}
@end