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

iOS UITableView Cell 復用導致按鈕狀態重置:正確的按鈕創建與狀態保持方案

     編輯:程式設計綜合問答

問題現象

在 cellForRowAtIndexPath 中為每個 Cell 動態創建 UIButton,點擊按鈕切換圖片(如關注/取消關注),但滾動列表後按鈕狀態恢復初始狀態。

錯誤代碼分析

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell4";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [self tableviewCellWithReuseIdentifierFollowing:CellIdentifier];
    }
    
    // 錯誤:每次獲取 cell 都重新創建按鈕並設置默認圖片
    followingButton = [UIButton buttonWithType:UIButtonTypeCustom];
    [followingButton setImage:[UIImage imageNamed:@"following12.png"] forState:UIControlStateNormal];
    // ... 添加 target、frame、tag ...
    [cell.contentView addSubview:followingButton];
    
    return cell;
}

核心錯誤:按鈕創建與默認圖片設置在 if (cell == nil) 之外,導致 Cell 復用時重複添加子視圖、重置圖片。

正確實現:按鈕創建放入 if (cell == nil)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell4";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        
        // 1. 僅創建一次按鈕
        UIButton *followingButton = [UIButton buttonWithType:UIButtonTypeCustom];
        followingButton.tag = 100; // 固定 tag 方便取回
        [followingButton addTarget:self action:@selector(followingButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
        [followingButton setImage:[UIImage imageNamed:@"following12.png"] forState:UIControlStateNormal];
        followingButton.frame = CGRectMake(220, 20, 100, 40);
        [cell.contentView addSubview:followingButton];
    }
    
    // 2. 獲取已存在的按鈕
    UIButton *followingButton = (UIButton *)[cell.contentView viewWithTag:100];
    
    // 3. 根據數據模型設置當前狀態(關鍵:同步模型狀態到 UI)
    MyDataModel *model = self.dataArray[indexPath.row];
    [followingButton setSelected:model.isFollowed]; // 模型驅動 UI
    [followingButton setImage:model.isFollowed ? [UIImage imageNamed:@"following_off12.png"] : [UIImage imageNamed:@"following12.png"] 
                                             forState:UIControlStateNormal];
    
    // 4. 存儲 indexPath 供點擊事件使用
    followingButton.tag = indexPath.row; // 復用 tag 傳遞行號
    
    return cell;
}

點擊事件處理:更新模型 + 刷新單行

- (void)followingButtonPressed:(UIButton *)sender {
    NSInteger row = sender.tag;
    MyDataModel *model = self.dataArray[row];
    
    // 1. 翻轉模型狀態
    model.isFollowed = !model.isFollowed;
    
    // 2. 可選:發送網絡請求,失敗時回滾 model.isFollowed
    
    // 3. 刷新對應行(最佳體驗)
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0];
    [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
    
    // 或僅更新按鈕(若確定 cell 可見)
    // UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
    // UIButton *btn = (UIButton *)[cell.contentView viewWithTag:100];
    // [btn setSelected:model.isFollowed];
}

核心原則總結

  1. 視圖創建一次:所有子視圖初始化放在 if (cell == nil) 內。
  2. 模型驅動視圖:Cell 配置階段 必須 根據數據模型設置按鈕當前狀態(selected、image、title 等)。
  3. 狀態改變更新模型:點擊事件先改模型,再刷新 UI(reloadRowsAtIndexPaths 或直接取 cell 更新)。
  4. 避免在 Cell 存儲業務狀態:Cell 可復用,業務狀態屬於 Model。

進階:自定義 Cell 類封裝

// FollowCell.h
@property (nonatomic, weak) IBOutlet UIButton *followButton;
- (void)configureWithModel:(MyDataModel *)model delegate:(id)delegate;

// FollowCell.m
- (void)configureWithModel:(MyDataModel *)model delegate:(id)delegate {
    self.followButton.selected = model.isFollowed;
    [self.followButton setImage:model.isFollowed ? offImage : onImage forState:UIControlStateNormal];
    // 使用 block 或 delegate 回調點擊,避免 tag 傳遞行號
    self.followButtonTapHandler = ^{
        model.isFollowed = !model.isFollowed;
        delegate?.didTapFollowButton(model);
    };
}

使用自定義 Cell + XIB/Storyboard + Block 回調,代碼更清晰、類型安全、易維護。

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