Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS - Detect UITableViewCell being moved out of the visible view?

As soon as the cell is no longer visible on the screen I need to be notified.

UITableView already has a delegate method called tableView:didEndDisplayingCell:forRowAtIndexPath: but this delegate method never gets called. And yes I do have the delegate for my UITableView set.

Any other ways to detect a cell being removed? I need to be able to save the content (inputs) of this cell before it's being reused by another item.

EDIT:

According to the documentation tableView:didEndDisplayingCell:forRowAtIndexPath: is iOS 6 and higher API. Is there a way to achieve this on iOS 5?

like image 948
aryaxt Avatar asked Dec 07 '12 21:12

aryaxt


3 Answers

On versions of iOS older than 6.0, the table view doesn't send the tableView:didEndDisplayingCell:forRowAtIndexPath: message.

If you are using a subclass of UITableViewCell, you can get the same effect on older versions of iOS by overriding didMoveToWindow:

- (void)didMoveToWindow {
    if (self.window == nil) {
        // I have been removed from the table view.
    }
}

You may need to give your cell a (weak or unsafe_unretained) reference back to your table view delegate so you can send the delegate a message.

However, you can't rely only on didMoveToWindow for all versions of iOS. Before iOS 6, a table view always removed a table view cell as a subview before reusing it, so the cell would always receive didMoveToWindow before being reused. However, starting in iOS 6, a table view can reuse a cell without removing it as a subview. The table view will simply change the cell's frame to move it to its new location. This means that starting in iOS 6, a cell does not always receive didMoveToWindow before being reused.

So you should implement both didMoveToWindow in your cell subclass, and tableView:didEndDisplayingCell:forRowAtIndexPath: in your delegate, and make sure it works if both are called, or if just one is called.

like image 58
rob mayoff Avatar answered Nov 05 '22 16:11

rob mayoff


I ended up using the combination of below to make sure that the logic applies to both iOS 5.0 and 6.0

CELL LOGIC

@protocol MyCellDelegate
- (void)myCellDidEndDisplaying:(MyCell *)cell;
@end

@implementation MyCell

// Does not work on iOS 6.0
- (void)removeFromSuperview
{
   [super removeFromSuperview];

   [self.delegate myCellDidEndDisplaying:(MyCell *)self];
}

@end

VIEWCONTROLLER LOGIC

@implementation MyViewcontroller

- (void)myCellDidEndDisplaying:(MyCell *)cell
{
   IndexPath *indexPath = [self.tableView indexPatForCell:cell];
   // do stuff
}

// Does not work on iOS below 6.0
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{

}

@end
like image 40
aryaxt Avatar answered Nov 05 '22 17:11

aryaxt


tableView:didEndDisplayingCell:forRowAtIndexPath:

is only available in iOS6 and later.

One (albeit slow) way to accomplish what you are after would be to use the scrollView delegate methods to monitor when the tableview scrolls. From there, call:

NSArray *visiblePaths = [tableView indexPathsForVisibleRows];

and check for any changes to the array of visible paths.

like image 33
Tim Avatar answered Nov 05 '22 16:11

Tim