Items mixed up after scrolling in UITableView
When I scroll in my UITableView, the cells become mixed up. What am I doing wrong?
This is my method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UI开发者_运维问答TableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
[cell insertSubview:[itemArray objectAtIndex:indexPath.row] atIndex:indexPath.row];
return cell;
}
Update
It now works by using cell.contentView, but now when I select an item, the selected one is overlayed with the content of a different cell...
TechZen's advice here is correct. It's clear from your code that you've misunderstood insertSubview:atIndex. I suspect that you probably also need a better understanding of when tableView:cellForRowAtIndexPath: does and doesn't called.
Unfortunately you've gotten some bad advice from sagar here, which may only confuse you further, especially because it may appear to work at first, but it will kill your scrolling performance and memory usage. For his benefit and yours, let me try to clarify tableView:cellForRowAtIndexPath: and the reuse identifier concept.
The key to understanding tableView:cellForRowAtIndexPath: and the reuse identifier is to understand that building a UITableViewCell is expensive. Consider all the things you need to do:
- Allocate a cell
- Allocate the cell's subviews.
- Define the layout of the subviews within the cell.
- Add the subviews to the cell.
- Configure properties of the subviews such as font sizes, colors, text wrapping, resizing behaviors, etc.
- Configure properties of the cell, such as accessory images, etc.
- Define the specific text and/or images that you want the cell to display.
When we create a table, we usually want the cells to have the same basic configuration. They'll typically have the same number of subviews, in the same positions, using the same fonts, etc. In fact, the only thing that usually needs to vary from one cell to the next is item 7 in the list above, the text and images displayed by the cell.
Steps one through six are quite expensive (especially the memory allocation), so it would kill our scrolling performance if we were to go through those steps for every cell we created, only to throw that cell away when it scrolls off the screen. It would be better if we could save the cell when it scrolls off the screen, and then just tweak its contents and reuse it for the next cell that we need to display.
Apple recognized the need for this cell reuse optimization, so they built a mechanism for it right into UITableView. When a cell scrolls off the screen, UITableView doesn't throw it away. Instead it looks at the cell's reuse identifier string, and puts the cell into a special buffer associated with that identifier. The next time you call dequeueReusableCellWithIdentifier: with that same identifier, UITableView will pull the cell out of its buffer and hand it back to you for reuse. This cell still has all the same subviews, in the same configuration as before, so all you need to do is step 7 in our list. Simply update the cell's text and/or images, and it's ready to go.
When you use this mechanism correctly, you'll only allocate one cell for each visible row, plus one for the buffer. No matter how many rows you have in your table, your memory usage will stay low, and your scrolling will be as smooth as butter.
Sagar recommended that you use a different reuse identifier for each row. Hopefully you can see why this is a bad idea. When each cell scrolls off the screen, the table view will look at the cell's identifier, see that it's unique, and create a new buffer for that specific row. If you scroll through 10,000 rows, your table view will end up with 10,000 buffers, each dedicated to a single cell. Your scrolling will be unnecessarily slow while you create 10,000 cell objects, and your app will probably run out of memory before you get to the bottom of the table.
So go ahead and keep your common cell identifier. Inside the if (cell == nil) { }
block, put all the setup code that would be common for all cells. Beneath that block, put only the code that populates the contents that are unique to each row. To access custom subviews whose contents you want to change per row, you can use -[UIView viewWithTag:], or better yet, create a subclass of UITableViewCell, and expose your custom subviews as properties of your subclass.
I think your problem here is that you are applying your row logic to the view hierarchy inside a cell instead of to the cells themselves.
This line:
[cell insertSubview:[itemArray objectAtIndex:indexPath.row] atIndex:indexPath.row];
Takes a view from an array and adds it to the cell's subviews at a particular index.row of the cell's existing subview stack. It does nothing to make sure the proper view is inserted in the proper cell itself. If you never remove the views from the previous iteration you will just see all these views stacking up within the individual reused cells.
At the very least, you need to remove all the previously added cell subviews before adding the most one. You should also only add subviews to the cell's contentView
view and not to the cell itself.
So:
[[cell.contentView.subviews objectAtIndex:0] removeFromSuperview];
[cell.contentView addSubview:[itemArray objectAtIndex:indexPath.row]];
精彩评论