Reputation: 12807
I have a UITableView where I have the backgroud color set via
UIView *myView = [[UIView alloc] init];
if ((indexPath.row % 2) == 0)
myView.backgroundColor = [UIColor greenColor];
else
myView.backgroundColor = [UIColor whiteColor];
cell.backgroundView = myView;
[myView release];
The problem I find is that when I edit a table (via setEditing:YES...) some cells of the same color invariable are next to each other. How do I force UITableView to fully redraw. reloadData is not doing a great job.
Is there are deep-cleaning redraw?
Upvotes: 6
Views: 6114
Reputation: 67
Use prepareForReuse() to reset the cell properties to its default values. The iOS calls this function before reusing a cell to give you a chance to reset it to its default values. Like this:
// put this in your TableViewCell class
override func prepareForReuse() {
super.prepareForReuse()
contentView.backgroundColor = .systemGroupedBackground // default background
}
Then set the cell's contentView in your ViewController, like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Your_cell_ID", for: indexPath)
if (indexPath.row % 2) == 0 {
cell.contentView.backgroundColor = .green
}
else {
cell.contentView.backgroundColor = .white
}
Upvotes: 0
Reputation: 3716
The accepted method seems dirty, it just makes a bunch of new cells that are stored along with the bad ones. Here are a couple of solutions depending on your situation:
1. first, for the situation described in the question you should not dump your cells and create new views on every cycle. You need to tag your view and then get it back when from the cell when you get a reuse cell:
- (UITableViewCell*) tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath {
UITableViewCell *cell = [tableView dequeueResuableCellWithIdentifier:SOME_ID];
if(!cell) {
cell = [[UITableViewCell alloc] init];
UIView *myView = [[UIView alloc] init];
cell.backgroundView = myView;
[myView setTag:5]; //<------
}
UIView *myView = [cell viewWithTag:5]; //<------
if ((indexPath.row % 2) == 0)
myView.backgroundColor = [UIColor greenColor];
else
myView.backgroundColor = [UIColor whiteColor];
return cell;
}
//then just reload the tableview.
2. ...or even better, why not just use the cell backgrouncolor and update that without creating a view.
3. A sure way to really clear out old cached cells it to simply recreate the UITableView object.
4. In most cases you dont need to destroy these cells, just keep track of your elements and update them after getting the reusable cell.You can tag all your elements, keep a array reference to them, find them thought the view hierarchy... Im sure theres a bunch of other ways.
5. heres a one liner to directly purge all cells, although not best practice to mess with the internals of objects like this as they might change in future versions:
[(NSMutableDictionary*)[tableview valueForKey:@"_reusableTableCells" ] removeAllObjects];
Upvotes: 1
Reputation: 7584
#define TABLE_VIEW_CELL_DEFAULT_ID @"cellIdentifier"
@property (nonatomic, strong) NSString *tableViewCellIdentifier;
@property (nonatomic) NSUInteger tableViewCellIdentifierCount;
// By using a different cell identifier, this effectively flushes the cell
// cache because the old cells will no longer be used.
- (void) flushTableViewCellCache
{
self.tableViewCellIdentifierCount++;
self.tableViewCellIdentifier = [NSString stringWithFormat:@"%@%i", TABLE_VIEW_CELL_DEFAULT_ID, self.tableViewCellIdentifierCount];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.tableViewCellIdentifier];
if (cell == nil) {
cell = [[MyTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:self.tableViewCellIdentifier];
}
// rest of method...
}
Upvotes: 0
Reputation: 116
I was able to solve this by adding a refresh variable to the table datasource. I used a dictionary for each cell, but there's an extra key called @"refresh":@"1", indicating the cell needs refreshing. Once it's updated, I set that key's value to @"0". So whenever the table is reloaded, make sure the key goes back to @"0" again.
Upvotes: 0
Reputation: 8298
For me the accepted answer didn't really work since I had no idea when to set the needsRefresh back to YES.
What worked for me was:
- (UITableViewCell*) tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath {
UITableViewCell *cell = [tableView dequeueResuableCellWithIdentifier:customCellIdentifier];
if(nil == cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:customCellIdentifier];
}
//.....
return cell;
}
And then you change the customCellIdentifier value whenever you need to. This way the cells are also still reusable if you switch back to the original cell identifier.
Upvotes: 3
Reputation: 163288
I had this issue before so I'll share with you how I solved it:
You can use a boolean flag (say it's called needsRefresh
) to control the behavior of cell creation in -cellForRowAtIndexPath
:
An example:
- (UITableViewCell*) tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath {
UITableViewCell *cell = [tableView dequeueResuableCellWithIdentifier:SOME_ID];
if(!cell || needsRefresh) {
cell = [[[UITableViewCell alloc] init....] autorelease];
}
//.....
return cell;
}
So, when you need a hard reload, set the needsRefresh
flag to YES
. Simple as a pimple.
Upvotes: 4