Overzealous Cell Reuse in Grid


#1

I have a structure that is a single column grid with nested Tables in each row.  It is structured like this:

HEADER
SECTION HEADER
Single Row (1 cell)
SECTION HEADER
Single Row (1 cell)
SECTION HEADER
Single Row (1 cell)

Inside of each of the data cells is a custom table, with a nested data grid inside.

The outer grid has a set of grids that it dynamically assigns to the inner custom cells, with code that looks like:

- (void)shinobiDataGrid:(ShinobiDataGrid *)grid prepareCellForDisplay:(SDataGridCell*) cell
{
  // All column values are ReportGridInnerTable objects so this is a safe cast
  ReportGridInnerTable* tableCell = (ReportGridInnerTable*)cell;
  NSInteger index = cell.coordinate.row.rowIndex;
  NSUInteger count = [self.dataGrids count];
  
  if (index < count) {
    ShinobiDataGrid* grid = self.dataGrids[index];
    [tableCell useGrid:grid];
  }
}

The individual nested cells look like:

@implementation ReportGridInnerTable

- (id)initWithReuseIdentifier:(NSString *)identifier
{
  if (self = [super initWithReuseIdentifier:identifier]) {
    // Remove any existing subviews
    [[self subviews]enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
      UIView* subview = (UIView*)obj;
      [subview removeFromSuperview];
    }];
  }
  
  return self;
}

-(void)resetForReuse
{
  self.grid = nil;
}

// We've been asked to size ourself. So apply all the locations required to our subviews.
- (void)setFrame:(CGRect)frame
{
  [super setFrame:frame];
  self.grid.frame = [self bounds];
}

-(void)useGrid:(ShinobiDataGrid *)grid
{
  self.grid = grid;
  [self addSubview:self.grid];
}

@end

 

 

The Problem:

I only get one of my custom cells ever created.  The sub table shows up right for one of the rows, but the others are blank (take up space, but not populated in any way).

In the debugger, I see each row attempting to get its appropriate nested cell, but each time it gets the same cell object passed to prepareCellForDisplay.

So each time that the cell’s initWithReuseIdentifier gets called, it wipes out the previous row’s settings, and goes to the new row.  The last row to ask for the cell object ends up winning, and the other rows have no content.

How do I make it pool additional objects?  I want each row to have its own copy of my ReportGridInnerTable class.   There appears to be near zero documentation on how object pooling works, and how to tweak its behavior as a user of the library.

Note:

* Every row is visible, and all sections are expanded. There is no scrolling or hiding happening.  Asking the grid for visible rows returns all the rows.


#2

Hi Cscheid,

The first thing that jumps out at me that could be an issue is that you are using your rowIndex to index your grids from your self.dataGrids array rather than the sectionIndex. Since each of these sections only have one row, the rowIndex property will always be 1. This means that you are actually adding the same grid to every cell, so it will be removing itself from its previous parent, and then adding itself to the latest cell you attempt to add it to, explaining the behaviour you are seeing (empty cells, except the last cell). Try doing the following instead and let me know if this works:

NSInteger index = cell.coordinate.row.sectionIndex;

There isn’t really any way you can tweak/modify the grids pooling as this is an internal implementation detail. The grid should always have enough cells in its pool, and if there aren’t enough then the grid should be creating more. If the grid isn’t doing this, then something is definitely wrong on our end. Please try the above and if you have no luck then we’ll look into the issue further.

Thanks,
Jan  :grin:


#3

Ahh! Thank you for seeing my bug.  So it wasn’t really about the cell pooling, and was instead that a given UIView can only have one parent.  So the later cell stole away the inner grid from the previous cell.

Thank you for helping!


#4

No problem Cschneid - glad I could help!