首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

iOS UITableViewCell 重用機制導致顯示錯誤:正確實作 dequeueReusableCellWithIdentifier

     編輯:程式設計疑難解答

UITableViewCell 重用是 iOS 效能優化核心,但錯誤實作會導致 Cell 內容顯示錯亂(如圖片閃爍、標籤錯位、資料錯誤)。本文解析重用池運作原理,並給出標準、無 Bug 的實作模板。

重用機制核心原理

  1. 呼叫 `dequeueReusableCellWithIdentifier:` 時,系統先從重用佇列取出閒置 Cell
  2. 若佇列為空,**自動建立新 Cell**(會呼叫 `initWithStyle:reuseIdentifier:`)
  3. Cell 離開可視區域時,**自動放回重用佇列**,**不會銷毀**
  4. 下一次相同 identifier 請求時,**直接取出舊 Cell 重用**

常見錯誤模式

錯誤代碼後果
每次 `cell = [[UITableViewCell alloc] init...]` 不用重用記憶體暴增、滾動卡頓
重用後**不清理/不重設**子視圖內容舊資料殘留、圖片閃爍、標籤錯位
在 `if (!cell)` 區塊外新增子視圖重用時疊加子視圖,導致視圖層級爆炸
用 `tag` 取子視圖但 tag 衝突取錯視圖、崩潰

標準無 Bug 實作模板(iOS 6+ / storyboard / xib / 純代碼通用)

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

進階:自訂 Cell 類別 + prepareForReuse

// 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;
}

關鍵檢查清單

  • ✅ `dequeueReusableCellWithIdentifier:` / `dequeueReusableCellWithIdentifier:forIndexPath:`
  • ✅ 子視圖**只在 `if (!cell)` / `initWithStyle` 中新增一次**
  • ✅ **每次** `cellForRowAtIndexPath` 都**完整重設**文字、顏色、圖片、狀態
  • ✅ 圖片非同步載入**必須取消舊請求**(`sd_cancelCurrentImageLoad` / `kf.cancelDownloadTask()`)
  • ✅ 自訂 Cell 實作 `prepareForReuse` 清理狀態
  • ✅ Identifier 唯一對應一種 Cell 結構,不同結構用不同 Identifier

遵循「建立時佈局、重用時重設、非同步取消舊請求」三原則,即可徹底解決 Cell 重用導致的顯示錯誤,獲得流暢 60fps 滾動體驗。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved