UITableViewCell 重用是 iOS 效能優化核心,但錯誤實作會導致 Cell 內容顯示錯亂(如圖片閃爍、標籤錯位、資料錯誤)。本文解析重用池運作原理,並給出標準、無 Bug 的實作模板。
| 錯誤代碼 | 後果 |
|---|---|
| 每次 `cell = [[UITableViewCell alloc] init...]` 不用重用 | 記憶體暴增、滾動卡頓 |
| 重用後**不清理/不重設**子視圖內容 | 舊資料殘留、圖片閃爍、標籤錯位 |
| 在 `if (!cell)` 區塊外新增子視圖 | 重用時疊加子視圖,導致視圖層級爆炸 |
| 用 `tag` 取子視圖但 tag 衝突 | 取錯視圖、崩潰 |
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellID = @"MyCellID"; // 唯一標識符
// 1. 取得/建立 Cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellID];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
// 2. **僅在建立時**新增子視圖、設定佈局、綁定 tag
UILabel *nameLabel = [[UILabel alloc] init];
nameLabel.tag = 100;
nameLabel.translatesAutoresizingMaskIntoConstraints = NO;
[cell.contentView addSubview:nameLabel];
// Auto Layout 约束...
}
// 3. **每次重用必執行**:重設所有動態內容
UILabel *nameLabel = (UILabel *)[cell.contentView viewWithTag:100];
MyModel *model = self.dataArray[indexPath.row];
nameLabel.text = model.name;
nameLabel.textColor = model.isHighlight ? [UIColor redColor] : [UIColor blackColor];
// 4. 圖片非同步載入:必須在 cellForRow 內部用 SDWebImage/Kingfisher 或自行實作 prepareForReuse
// 錯誤:cell.imageView.image = nil; // 不夠,需取消舊請求
// 正確:使用 SDWebImage
[cell.imageView sd_setImageWithURL:model.imageURL placeholderImage:[UIImage imageNamed:@"placeholder"]];
return cell;
}
// MyCustomCell.h
@interface MyCustomCell : UITableViewCell
@property (nonatomic, strong) IBOutlet UILabel *nameLabel;
@property (nonatomic, strong) IBOutlet UIImageView *avatarView;
- (void)configureWithModel:(MyModel *)model;
@end
// MyCustomCell.m
- (void)prepareForReuse {
[super prepareForReuse];
self.nameLabel.text = nil;
self.avatarView.image = nil;
self.avatarView.sd_cancelCurrentImageLoad; // 取消舊圖片下載
}
- (void)configureWithModel:(MyModel *)model {
self.nameLabel.text = model.name;
[self.avatarView sd_setImageWithURL:model.imageURL placeholderImage:[UIImage imageNamed:@"placeholder"]];
}
// Controller
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyCustomCell" forIndexPath:indexPath];
[cell configureWithModel:self.dataArray[indexPath.row]];
return cell;
}
遵循「建立時佈局、重用時重設、非同步取消舊請求」三原則,即可徹底解決 Cell 重用導致的顯示錯誤,獲得流暢 60fps 滾動體驗。