解决点击UITableViewCell按钮切换图片后滚动时其他按钮异常变更问题
Hey there, let's sort out this annoying cell reuse issue you're dealing with! The root problem here is that UITableView reuses cells to keep things memory-efficient—so when you scroll, an old cell that was used for a previous row gets repurposed for a new one. If you don't tie the button's state to your data model, those old button images stick around and mess up other rows. Here's how to fix it properly:
Step 1: Add a State Property to Your Data Model
First, you need a way to track the button's state for each row. Add a boolean property to whatever class you're using for your table data (let's say it's called ListItem):
// In your ListItem.h file @property (nonatomic, assign) BOOL isButtonActive; // Name this something that makes sense for your app, like isLiked, isBookmarked, etc.
This will store whether the button should be in its "on" or "off" state for each individual row.
Step 2: Update Your Custom Cell to Sync with the Model
In your ListTableViewCell, make sure the button's state gets set based on the data model, and that when the button is tapped, it updates both the cell's appearance and the data source.
First, add a way to pass the index path and a callback to the cell (we'll use a block here, but a delegate works too):
// ListTableViewCell.h typedef void(^ButtonTapHandler)(NSIndexPath *indexPath); @property (nonatomic, strong) NSIndexPath *cellIndexPath; @property (nonatomic, copy) ButtonTapHandler onButtonTap;
Then, in the cell's button tap action, toggle the state and trigger the callback to update the data source:
// ListTableViewCell.m - (IBAction)toggleButtonTapped:(UIButton *)sender { // First, toggle the button's image locally BOOL newState = !sender.isSelected; sender.selected = newState; [sender setImage:newState ? [UIImage imageNamed:@"toggled-on"] : [UIImage imageNamed:@"toggled-off"] forState:UIControlStateNormal]; // Tell the view controller to update the data model if (self.onButtonTap) { self.onButtonTap(self.cellIndexPath); } }
Step 3: Correctly Configure Cells in cellForRowAtIndexPath:
This is the crucial part—every time a cell is reused, you need to reset its button state based on the data model, not just rely on the cell's previous state. Here's how to adjust your existing method:
- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath { ListTableViewCell *cell = (ListTableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"YourCellIdentifier" forIndexPath:indexPath]; // Grab the data model for this row ListItem *currentItem = self.yourDataArray[indexPath.row]; // Set the cell's index path so we know which row to update when the button is tapped cell.cellIndexPath = indexPath; // Set the button's state based on the data model BOOL shouldBeSelected = currentItem.isButtonActive; cell.yourToggleButton.selected = shouldBeSelected; [cell.yourToggleButton setImage:shouldBeSelected ? [UIImage imageNamed:@"toggled-on"] : [UIImage imageNamed:@"toggled-off"] forState:UIControlStateNormal]; // Set up the tap handler to update the data model when the button is pressed cell.onButtonTap = ^(NSIndexPath *path) { ListItem *updatedItem = self.yourDataArray[path.row]; updatedItem.isButtonActive = !updatedItem.isButtonActive; }; // Configure other cell content here... return cell; }
Why This Works
Before, when cells were reused, they kept the button image from the last time they were used. Now, every time a cell is assigned to a row, we pull the correct state directly from your data model and set the button accordingly. When the button is tapped, we update the data model first—so even if the cell gets reused later, it'll pick up the correct saved state.
内容的提问来源于stack exchange,提问作者Simeryn Denis

