Reputation: 5290
I have UITableView
which uses custom UITableViewCell
s. The cells can have one of three types of background images (set in each cell's .backgroundView.image
property): top, middle, or bottom. The top and bottom images are for the first and last cells, and have rounded corners (much like a grouped UITableView
's cells). All other "middle" cells within the UITableView
have a rectangular middle background image.
My problem is that when reordering the cells in the UITableView
's Edit mode the cells do not refresh with a different background image depending on their new location. For example, if I move the first cell into the middle of the table it still retains its original "top" background image (with rounded corners) and the new cell which appears at the top of the table view still has its original "middle" background image, which all looks a bit odd.
I can get around this by doing a reloadData
on the table view, but this has the problem of not giving a nice graceful animation of the changes. I noticed that when reordering a standard grouped UITableView
as soon as a cell is moved / dragged the background image on the relevant cells change (even before the moved cell has been dropped into its new location) which looks very nice. I would like to achieve the same thing.
To this end, I have implemented tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath
which is called every time a row is dragged around in the table view. Within this I have tried various methods of setting the backgroundView.image
including just directly setting the image and then also explicitly telling it to redraw the cell and/or image using setNeedsLayout
and setNeedsDisplay
but nothing seems to make the cell redraw. I have NSLog
'ged the results of these changes and they appear to be committing to the cell, as the correct values appear in the NSLog
, but the cell just does not seem to be updating on screen.
If anyone could point out what I am doing wrong, or suggest a better way of achieving a working outcome, I would be very much appreciative. Thanks!
EDIT: the following code was pulled from the bounty and formatted so it can be more easily digested:
UIImage *rowBackground;
UIImage *selectionBackground;
NSInteger sectionRows = [_tableView numberOfRowsInSection:[indexPath section]];
NSInteger row = [indexPath row];
if (row == 0 && row == sectionRows - 1)
{
rowBackground = [UIImage imageNamed:@"field_title_bkg.png"];
selectionBackground = [UIImage imageNamed:@"field_title_bkg.png"];
}
else if (row == 0)
{
rowBackground = [UIImage imageNamed:@"table_row_top_bkg.png"];
selectionBackground = [UIImage imageNamed:@"table_row_top_bkg.png"];
}
else if (row == sectionRows - 1)
{
rowBackground = [UIImage imageNamed:@"table_bottom_row_bkg.png"];
selectionBackground = [UIImage imageNamed:@"table_bottom_row_bkg.png"];
}
else
{
rowBackground = [UIImage imageNamed:@"table_row_bkg.png"];
selectionBackground = [UIImage imageNamed:@"table_row_bkg.png"];
}
UIImageView *rowBackGroundImageView = [[UIImageView alloc] initWithImage:rowBackground];
UIImageView *rowBackGroundSelectionImageView = [[UIImageView alloc] initWithImage:selectionBackground];
if (row != sectionRows - 1)
{
UIImageView *sep = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cell_separator.png"]];
[sep setFrame:CGRectMake(20, 56, 280, 1)];
[rowBackGroundImageView addSubview:sep];
}
[cell setBackgroundView:rowBackGroundImageView];
[rowBackGroundImageView release];
Upvotes: 8
Views: 4157
Reputation: 17186
Write below code in your project and it should work as per expectations.
- (NSIndexPath *)tableView:(UITableView *)tableView
targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath
toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath
{
NSInteger sectionRows = [tableView numberOfRowsInSection:[sourceIndexPath section]];
UITableViewCell *sourceCell = [tableView cellForRowAtIndexPath:sourceIndexPath];
UITableViewCell *destCell = [tableView cellForRowAtIndexPath:proposedDestinationIndexPath];
if(sourceIndexPath.row == 0 && proposedDestinationIndexPath.row == 1)
{
((UIImageView *)destCell.backgroundView).image = [UIImage imageNamed:@"table_row_top_bkg.png"];
if(proposedDestinationIndexPath.row == sectionRows - 1)
((UIImageView *)sourceCell.backgroundView).image = [UIImage imageNamed:@"table_bottom_row_bkg.png"];
else
((UIImageView *)sourceCell.backgroundView).image = [UIImage imageNamed:@"table_row_bkg.png"];
}
else if(sourceIndexPath.row == sectionRows - 1 && proposedDestinationIndexPath.row == sectionRows - 2)
{
((UIImageView *)destCell.backgroundView).image = [UIImage imageNamed:@"table_bottom_row_bkg.png"];
if(proposedDestinationIndexPath.row == 0)
((UIImageView *)sourceCell.backgroundView).image = [UIImage imageNamed:@"table_row_top_bkg.png"];
else
((UIImageView *)sourceCell.backgroundView).image = [UIImage imageNamed:@"table_row_bkg.png"];
}
else if(proposedDestinationIndexPath.row == 0)
{
((UIImageView *)sourceCell.backgroundView).image = [UIImage imageNamed:@"table_row_top_bkg.png"];
destCell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:proposedDestinationIndexPath.section]];
if(sectionRows == 2)
((UIImageView *)destCell.backgroundView).image = [UIImage imageNamed:@"table_bottom_row_bkg.png"];
else
((UIImageView *)destCell.backgroundView).image = [UIImage imageNamed:@"table_row_bkg.png"];
}
else if(proposedDestinationIndexPath.row == sectionRows - 1)
{
((UIImageView *)sourceCell.backgroundView).image = [UIImage imageNamed:@"table_bottom_row_bkg.png"];
destCell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:sectionRows - 1 inSection:proposedDestinationIndexPath.section]];
if(sectionRows == 2)
((UIImageView *)destCell.backgroundView).image = [UIImage imageNamed:@"table_row_top_bkg.png"];
else
((UIImageView *)destCell.backgroundView).image = [UIImage imageNamed:@"table_row_bkg.png"];
}
return proposedDestinationIndexPath;
}
Upvotes: 5
Reputation: 292
the apis for partially reloading rows in the ios sdk doesn't function well (that's why ppl follow this post here)
I end up using timer to reload the whole data after reordering animation finishes. It works for me.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
//things to be done when reordering rows
....
//issue a timer to reload data
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval: 0.1
target: self
selector:@selector(updateRow)
userInfo: nil repeats:NO];
}
-(void) updateRow
{
[self.table reloadData];
}
Upvotes: 0
Reputation: 292
(this solution only works for the reordering of rows within the same section. It'll fail when dragging and dropping rows to a different section)
I had the same problem when updating a cell's detail label text after being reordered.
I solved it with the following code:
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
UITableViewCell* cell = [self.table cellForRowAtIndexPath:fromIndexPath];
cell.detailTextLabel.text = @"NEW STRING";
}
Note that you have to update the cell at fromIndexPath
, because it seems that the internal state of the tableview doesn't update until this method returns. If you get the cell at toIndexPath
, after reordering finishes, you will see the neighbor cell gets updated. (either top or bottom one).
Upvotes: 0
Reputation: 19251
Try this:
[tableView beginUpdates];
[tableView endUpdates];
Upvotes: 1
Reputation: 10070
If you just want to reload a few cells rather the entire table you can use:
reloadRowsAtIndexPaths:withRowAnimation:
available as an instance method of UITableView.
Upvotes: 1
Reputation: 18670
I haven't tried myself but why not test your indexPath.row value on the cellForRowAtIndexPath method and provide the different backgroundView.image values for first and last row?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
I hope this helps. Cheers, Rog
Upvotes: 0