UI学习—cell的复用和自定义cell

前言

  • Nib是什么?

Nib就是.xib文件:一个可视化的UI界面文件,它记录了一个UI组件(例如一个表格单元格Cell)的界面布局信息,可以在interfaceBuilder中创建

objective-c 复制代码
[UINib nibWithNibName:@"CustomCell" bundle:nil]

这段代码的意思就是加载一个名叫CustomCell.xib的XIB文件,把它变成UINib对象么,准备好以后注册给其他对象使用

参数介绍 :

1.@"CustomCell":XIB文件名,不可以带.xib后缀

  1. bundle:nil :默认从主包加载

介绍

cell复用和自定义cell是在开发iOS应用时常见的优化技巧和定制需求,cell复用是UITableView或UIColletionView的一个重要‼️的优化机制。当用户滚动这些视图时,只有少量可见的cell会被创建和显示出来,对于那些不可见的cell,徐系统会将他们缓存起来以备将来使用。当用户在滚动屏幕的时候,屏幕上的cell只是所有数据的一小部分,当某个cell滚动出屏幕时,系统会将其放入一个队列中等待复用,当需要显示新的cell时,系统会首先检查这个队列,看是否有可以复用的cell,如果有就直接使用,如果没有才会创建新的cell。在实现cell的复用时,需要给cell设定不同的复用标识符(reuse identifier),然后在需要新的cell时,使用这个标识符去请求,如果队列中有可复用的cell,系统就会返回一个,否则就会创建新的cell,标识符的设定,使得我们可以为不同类型的cell设定不同的复用标识符,从而在同一个表视图或者集合视图中使用多种类型的cell。

复用原理

当创建UITableView并加载数据时,系统会实现如下操作

  1. 创建缓存结构:

    1. 一个字典:用于缓存cell的类与标识符的关系
    2. 一个数组:记录每个section的可见行信息
    3. 一个可变集合mutableSet:专门存放可以复用的cell
  2. 加载首屏cell(n + 1)个

    1. 他会调用
    objc 复制代码
    [dataSource tableView:cellForRowAtIndexPath:]
    1. 并通过
    objective-c 复制代码
    [tableView dequeueReusableCellWithIdentifier:@"MyCellID"]

    去查看是否有可复用的cell?

  3. cell滚出屏幕时,系统就会把他放进reuse pool中,而不是释放掉

  4. 向下滚动时,取出reuse pool 的cell

UITableView中cell复用方式

手动(非注册)

  • 什么是非注册类型的?

非注册类型 = 没有使用registerClass:或registerNib:来注册cell,而是手动判断并创建的方式

  • 使用方式
objective-c 复制代码
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
	
    static NSString *identifier = @"MyCellID";

    // 从复用池中获取
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];

    // 如果没有可复用的 cell,则创建一个新的
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
        // 只初始化一次的配置可以写在这里
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
    }

    // 每次都要重新配置内容
    cell.textLabel.text = [NSString stringWithFormat:@"Row %ld", indexPath.row];

    return cell;
}
  • 原理分析

UITableview会从内部的复用池寻找标识符为identifier的cell,如果没有可复用的cell,就返回nil,此时需要手动创建一个新的并指定相同的reuseIdentifier,系统会在滚动后自动将cell回收到复用池中

  • 使用场景推荐
    • 完全动态的cell类型
objective-c 复制代码
NSString *identifier = [NSString stringWithFormat:@"Section_%ld", indexPath.section];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
}

目的:每个section使用一个不同的复用标识符

自动(注册类型)

  • 什么是注册类型?

注册类型 = 提前告诉UITableView或UICollectionView:会使用哪一种cell(类或nib)来复用

注册后,系统会在你调用 dequeueReusableCellWithIdentifier:forIndexPath: 时,自动为你创建或复用cell,不再需要手动写 if (!cell)

  • 注册方式
  1. 注册类
objective-c 复制代码
[tableView registerClass:[MyCustomCell class] forCellReuseIdentifier:@"MyCellID"];

告诉系统会使用MyCustomCell这个类作为cell,reuseIdentifier是"MyCellID"

这个类必须是UITableViewCell的子类,且支持用init方法初始化,纯代码模式,不依赖.xib文件

适用于cell布局不复杂,可以通过AutoLayout或frame代码快速搭建,不需要使用Interface Builder

  1. 注册XIB(nib文件方式)
objective-c 复制代码
UINib *nib = [UINib nibWithNibName:@"MyCustomCell" bundle:nil];
[tableView registerNib:nib forCellReuseIdentifier:@"MyCellID"];

告诉系统会使用一个名为MyCustomCell.xib的nib文件创建cell

nib里要设置cell的class是自定义类的UITableViewCell子类

  • 如何使用已注册的cell?
objective-c 复制代码
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyCellID" forIndexPath:indexPath];
    
    // 配置 cell 的内容
    cell.titleLabel.text = self.dataArray[indexPath.row];
    
    return cell;
}

自定义cell的实现

如果要实现自定义cell需要实现两个协议

UItableViewDelegate和UITableViewDataSource

前者:

objective-c 复制代码
//即将显示道屏幕上时调用
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;
//当某个section的Header即将显示时调用
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section API_AVAILABLE(ios(6.0));
//某个 section 的 Footer 即将显示时调用
- (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section API_AVAILABLE(ios(6.0));
//当某个cell即将滑出屏幕并结束显示时调用
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath API_AVAILABLE(ios(6.0));
//某个header滑出屏幕后调用
- (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section API_AVAILABLE(ios(6.0));
//某个footer滑出屏幕后调用
- (void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section API_AVAILABLE(ios(6.0));
//返回某一行的高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
//返回每个section的header高度
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
//设置每个section的footer高度
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;

后者:

objective-c 复制代码
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;

// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

使用原因:

  1. 个性化设计需求,当系统提供的单元格样式无法满足应用的设计需求时,就需要通过自定义单元格来实现特定的界面和布局
  2. 复杂的数据展示:如果需要在单元格中显示复杂的数据结构(如图文混排、动态控件集合等),使用自定义单元格可以更灵活的控制数据的展示方式
  3. 优化性能:当列表视图中需要展示的单元格类型非常多样或者数据加载非常复杂时,通过精心设计的自定义单元格可以有效的提高滚动和渲染的性能

在自定义cell时,我们需要新建一个UITableViewCell类,在这个类的h文件中添加我们所需要的相关控件,在m文件中重写初始化initWithStyle:reuseldenifier:方法,在该方法中,首先使用父类的init方法初始化,以创建一个合法的cell,然后手动为该self的各个属性赋值,最后返回self即可

注意:在需要将自定义控价先添加道self.contentView上,这个contentView就是cell的子视图,是内容视图

  • 自定义cell
objective-c 复制代码
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface MyCustomCell : UITableViewCell
@property(nonatomic, strong) UILabel* titleLabel;
@end

NS_ASSUME_NONNULL_END
objective-c 复制代码
#import "MyCustomCell.h"

@implementation MyCustomCell

//这是UITableView的指定初始化方法
/*
 参数讲解
 参数一:cell的样式
 参数二:用于cell的复用机制,标记这个cell的身份
 */
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    //调用父类的UITableView的构造方法,初始化基础内容,必须写,否则无法获得一个合法的cell实例
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        self.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 10, 300, 30)];
        self.titleLabel.textColor = [UIColor orangeColor];
        //contentView是UITbableCell内部的容器视图,应该把所有UI元素加到它上面,而不是直接加到cell本身
        [self.contentView addSubview:self.titleLabel];
    }
    return self;
}

- (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
}

@end
  • 实现
objective-c 复制代码
#import "ViewController.h"
#import "MyCustomCell.h"

@interface ViewController () <UITableViewDelegate, UITableViewDataSource>

@property (nonatomic, strong) UITableView *tableView;

@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
    self.tableView.delegate = self;
    self.tableView.dataSource = self;
    
    [self.tableView registerClass:[MyCustomCell class] forCellReuseIdentifier:@"MyCellID"];
    
    [self.view addSubview:self.tableView];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 20;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MyCustomCell* cell = [tableView dequeueReusableCellWithIdentifier:@"MyCellID"forIndexPath:indexPath];
    cell.titleLabel.text = [NSString stringWithFormat:@"Row:%ld",indexPath.row];
    return cell;;
}
@end
  • 在实现文件中实现一个layoutSubViews方法来设置各个空间的位置
相关推荐
萌新小码农‍5 小时前
Spring框架学习day7--SpringWeb学习(概念与搭建配置)
学习·spring·状态模式
蓝婷儿5 小时前
6个月Python学习计划 Day 15 - 函数式编程、高阶函数、生成器/迭代器
开发语言·python·学习
行云流水剑5 小时前
【学习记录】深入解析 AI 交互中的五大核心概念:Prompt、Agent、MCP、Function Calling 与 Tools
人工智能·学习·交互
海尔辛6 小时前
Unity UI 性能优化--Sprite 篇
ui·unity·性能优化
一弓虽6 小时前
zookeeper 学习
分布式·学习·zookeeper
苗老大6 小时前
MMRL: Multi-Modal Representation Learning for Vision-Language Models(多模态表示学习)
人工智能·学习·语言模型
xhyu617 小时前
【学习笔记】On the Biology of a Large Language Model
笔记·学习·语言模型
小白杨树树7 小时前
【SSM】SpringMVC学习笔记7:前后端数据传输协议和异常处理
笔记·学习
二流小码农9 小时前
鸿蒙开发:实现一个标题栏吸顶
android·ios·harmonyos