adding KVO to UITableViewCell

Z S picture Z S · Oct 5, 2011 · Viewed 11.3k times · Source

I have a custom UITableViewCell which is displaying various attributes of a Person object (backed by Core Data) ... some labels, images etc. I currently force the whole tableview to reload whenever any property changes, and that's obviously not efficient. I know with KVO, I should be able to add a listener to a label in the cell that can listen for changes in the Person's properties. But I'm not sure how to implement it and can't find any examples.

Here's what I typically do in my UITableView's cellForRowAtIndexPath:

    - (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath
    {
        static NSString *simple = @"CustomCellId";

        CustomCell *cell = (CustomCell *) [tableView dequeueReusableCellWithIdentifier:simple];

        if (cell == nil)
        {
            NSArray *nib =  [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];

            for (id findCell in nib )
            {
                if ( [findCell isKindOfClass: [CustomCell class]])
                {
                    cell = findCell;
                }    
            }
         }
         Person *managedObject = [self.someArray objectAtIndex: indexPath.row];
         cell.namelabel.text =  managedObject.displayName;
         return cell;
}

The cell is hooked up in IB. I would want to detect when displayName changes, and update just the name label. Thanks

Answer

Robert Karl picture Robert Karl · Aug 21, 2013

The above answer is great for static cells. Using KVO for UITableViewCells still works with cell reuse. Add the observers you need when the cell is about to appear, and remove them when the cell is no longer displayed. The only trick is that Apple seems to be inconsistent about sending didEndDisplayingCell:, so observers need to be removed in two places on iOS 6.1

@implementation MyTableViewCell

@property MyTableViewController * __weak parentTVC;

- (UITableViewCell *)tableView:(UITableView *)tableView 
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    ((MyTableViewCell *)cell).parentTVC = self;
    // Don't add observers, or the app may crash later when cells are recycled
}


- (void)tableView:(UITableView *)tableView 
  willDisplayCell:(HKTimelineCell *)cell 
forRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Add observers
}

- (void)tableView:(UITableView *)tableView 
didEndDisplayingCell:(UITableViewCell *)cell 
forRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self removeMyKVOObservers];
}

- (void)viewWillDisappear:(BOOL)animated
{
    for (MyTableViewCell *cell in self.visibleCells) {
        // note! didEndDisplayingCell: isn't sent when the entire controller is going away! 
        [self removeMyKVOObservers];
    }
}

The following can occur if observers aren't cleaned up. The observer might try to notify whatever object is at that memory location, which may not even exist.

<NSKeyValueObservationInfo 0x1d6e4860> ( <NSKeyValueObservance 0x1d4ea9f0: Observer: 0x1d6c9540, Key path: someKeyPath, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x1c5c7e60> <NSKeyValueObservance 0x1d1bff10: Observer: 0x1d6c9540, Key path: someOtherKeyPath, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x1c588290>)