【iOS】cell的復用以及自定義cell
文章目錄
- 【iOS】cell的復用以及自定義cell
- 前言
- cell的復用
- 手動(非注冊)
- 自動(注冊)
- 自定義cell
前言
- cell的復用及自定義cell是UITableView或UICollectionView的一個重要優化機制,當用戶滾動視圖時,只有少量可見的cell會被創建與顯示,暫時不可見的,都會緩存起來以備后面復用,這個機制主要是為了提高應用性能。(創建與銷毀都是相對開銷高的操作,通過復用cell,可以避免不必要的視圖創建與銷毀,從而提高應用的滾動性能)
UICollectionView = 更自由、更強大的“多列列表”控件,適合做復雜的網格/橫滑頁面,如相冊等
- 自定義cell可以更好的控制cell的外觀和行為,提高代碼的可讀與維護性,自定義cell的步驟主要有:創建自定義cell類(繼承于UITableViewCell),添加UI元素(如標簽,按鈕等),設置cell的布局(即調整組數與行數或者位置),及在TableView中使用自定義Cell。
實際開發里,我們通常會結合使用cell復用與自定義cell,可以達到優化性能和滿足特定需求的目的
cell的復用
cell復用的主要概念前面已經講了,下面主要介紹其內容,cell的復用主要有兩種方法,分為自動(注冊)和手動復用(非注冊),現在更推薦自動的方式。二者的主要區別在于是否注冊復用標識符,以及使用了哪個方法來取cell。
手動(非注冊)
不用注冊 cell,而是自己判斷是否有復用的 cell,如果沒有就手動創建一個。
-
在 cellForRowAtIndexPath中使用 dequeueReusableCellWithIdentifier:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellID"];
這行代碼會從復用池中拿出一個標識為 “cellID” 的 cell,如果有就復用,如果沒有,返回 nil
-
判斷cell是否是nil如果是就手動創建
if (!cell) {cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cellID"]; }
這里你必須手動 alloc/init 一個 cell,否則返回的是空的,程序會崩潰
-
配置cell的內容
cell.textLabel.text = [NSString stringWithFormat:@"第 %ld 行", indexPath.row];
代碼示例:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {// 第一步:嘗試從復用池取 cellUITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellID"];// 第二步:如果沒有可復用的,就手動 new 一個if (!cell) {cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cellID"];}// 第三步:配置 cell 內容cell.textLabel.text = [NSString stringWithFormat:@"第 %ld 行", indexPath.row];return cell;
}
自動(注冊)
只需要正確地設置復用標識符并在需要時請求復用的cell 主要步驟如下:
-
注冊cell
在使用 tableView 前(通常在
viewDidLoad
中),注冊 cell 的類文件:[self.tableView registerClass:[MyTableViewCell class] forCellReuseIdentifier:@"cellID"];
-
在 cellForRowAtIndexPath中獲取復用的 Cell 使用 dequeueReusableCellWithIdentifier:forIndexPath:獲取 cell,無需手動判斷 nil:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {// 自動復用,不用判斷 cell 是否為nilMyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cellID" forIndexPath:indexPath];return cell;
}
自定義cell
前面提到自定義cell是在開發iOS應用時常用的一種方式,可以更好的控制cell的外觀和行為
一般來說要實現自定義cell需要先實現兩個協議:UITableViewDelegate和 UITableViewDataSource
UITableViewDelegate這個協議主要用于實現顯示單元格,設置單元格的行高和對于制定的單元格的操作設置頭視圖和尾視圖。這個協議中沒有必須完成的方法,里面的都是可選方法
有如下方法:
// Display customization- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section API_AVAILABLE(ios(6.0));
- (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section API_AVAILABLE(ios(6.0));
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath API_AVAILABLE(ios(6.0));
- (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section API_AVAILABLE(ios(6.0));
- (void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section API_AVAILABLE(ios(6.0));// Variable height support- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;
UITableViewDataSource
有以下兩個必須實現的方法:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
//返回指定分區的行數
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
//為指定索引路徑(indexPath)創建并配置單元格。
除此之外還有很多可選的方法,舉出主要的幾個:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
//確定表格視圖里分區(section)的數量。若你沒有實現該方法,系統會默認返回 1 個分區。
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;
//為指定分區設置文本形式的頭部標題。若不實現該方法,分區將沒有頭部標題。
- (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section;
//為指定分區設置文本形式的底部標題。若不實現該方法,分區將沒有底部標題。- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath;
//判斷指定行是否可以進入編輯狀態(如刪除、插入等操作)。若不實現該方法,系統默認所有行都可以編輯。
自定義cell主要的步驟包括:
- 創建自定義cell類:首先,需要創建一個新的類,通常會繼承自UITableViewCell或UICollectionViewCell
- 添加UI元素:在這個自定義cell類中,我們可以添加你需要的UI元素或者屬性,如UILabel,UIImageView等。
- 實現初始化方法:在自定義cell類的初始化方法中,需要初始化我們添加的UI元素,并添加到cell的contentView上。
- 設置cell的布局:還需要在自定義cell類中設置UI元素的布局,可以使用自己設置的方法layout來進行自己的布局
- 在TableView中使用自定義cell:在TableView的tableView(_:cellForRowAt:)方法中,我們需要先通過復用標識符嘗試獲取一個可復用的cell,如果沒有獲取到,那么就創建一個新的自定義cell實例,并返回。
代碼示例:
MyTableViewCell.h
#import <UIKit/UIKit.h>NS_ASSUME_NONNULL_BEGIN@interface MyTableViewCell : UITableViewCell@property (nonatomic, strong) UILabel *label;
@property (nonatomic, strong) UIButton *button;
@property (nonatomic, strong) UIImageView *ImageView;@endNS_ASSUME_NONNULL_END
MyTableViewCell.m
#import "MyTableViewCell.h"@implementation MyTableViewCell- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];if (self) {self.label = [[UILabel alloc] init];self.label.font = [UIFont systemFontOfSize:16];self.label.textColor = [UIColor blackColor];[self.contentView addSubview:self.label];self.button = [UIButton buttonWithType:UIButtonTypeSystem];[self.button setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];[self.contentView addSubview:self.button];self.ImageView = [[UIImageView alloc] init];self.ImageView.contentMode = UIViewContentModeScaleAspectFit;self.ImageView.clipsToBounds = YES;[self.contentView addSubview: self.ImageView];}return self;
}- (void)layoutSubviews {[super layoutSubviews];self.ImageView.frame = CGRectMake(10, 10, 60, 60);self.label.frame = CGRectMake(80, 15, 200, 30);self.button.frame = CGRectMake(self.contentView.bounds.size.width - 80, 15, 60, 40);
}@end
ViewController.h
#import <UIKit/UIKit.h>@interface ViewController : UIViewController<UITableViewDelegate, UITableViewDataSource>@property (nonatomic, strong) UITableView *tableView;@end
ViewController.m
#import "ViewController.h"
#import "MyTableViewCell.h"static NSString *str = @"id";@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped];self.tableView.delegate = self;self.tableView.dataSource = self;self.tableView.backgroundColor = [UIColor grayColor];[self.tableView registerClass:[MyTableViewCell class] forCellReuseIdentifier:str];[self.view addSubview:self.tableView];
}- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {return 4;
}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {switch (section) {case 0:return 2;case 1:return 4;case 2:return 1;case 3:return 3;default:return 0;}
}- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {return 80;
}- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:str forIndexPath:indexPath];cell.label.text = [NSString stringWithFormat:@"第 %ld 組 第 %ld 行", indexPath.section + 1, indexPath.row + 1];[cell.button setTitle:@"點擊" forState:UIControlStateNormal];NSString *imageName = [NSString stringWithFormat: @"ima%lu.jpg", indexPath.section + 1];cell.ImageView.image = [UIImage imageNamed: imageName];return cell;
}@end