0

I have the following subclass of UICollectionViewFlowLayout:

@implementation MyFlowLayout

- (instancetype)init {
  self = [super init];
  self.stickyIndexPaths = [NSMutableArray array];
  return self;
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
  return YES;
}

- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
  CGRect collectionViewArea = CGRectMake(0.f, 0.f, self.collectionView.contentSize.width, self.collectionView.contentSize.height);
  NSArray *attributes = [super layoutAttributesForElementsInRect:collectionViewArea].copy;
  for (UICollectionViewLayoutAttributes *item in attributes) {
    [self makeStickyIfNeeded:item];
  }

  return attributes;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
  UICollectionViewLayoutAttributes *item = [super layoutAttributesForItemAtIndexPath:indexPath];
  [self makeStickyIfNeeded:item];
  return item;
}

- (void)makeStickyIfNeeded:(UICollectionViewLayoutAttributes*)item {
  BOOL indexPathIsSticky = [self.stickyIndexPaths indexOfObjectPassingTest:^BOOL(NSIndexPath *i, NSUInteger idx, BOOL *stop) {
    *stop = [i compare:item.indexPath] == NSOrderedSame;
    return *stop;
  }] != NSNotFound;
  if (indexPathIsSticky) {
    if (self.collectionView.contentOffset.y > item.frame.origin.y) {
      CGRect f = item.frame;
      f.origin.y = self.collectionView.contentOffset.y;
      item.frame = f;
      item.zIndex = 1.f;
    }
  }
  else {
    item.zIndex = 0.f;
  }
}

From the above, the idea is that I can add indexPaths of cells and make them 'stick' to the top (assuming a vertical flow), while any other cells below a sticky cell keep scrolling underneath. I'm testing this code with a single sticky cell, below which I have, say, 30 other cells (enough to for the content offset to be greater than the sticky cell's position, which will cause it to stick while the rest of the cells scroll underneath it). So far so good.

But I've noticed 2 side-effects of my flow subclass:

  1. Sometimes, when the UICollectionView first appears, the cells underneath the sticky cell don't appear. If I scroll the collection view a little bit, then they appear.
  2. If I scroll all the way to the last cell (remember this implies some cells have scrolled below the sticky one at this point), then remove one cell from my data source and do a -reloadData, while I get the correct number of cells (one less than before), the sticky header is no longer visible. And similar to the first case, if I nudge the collection view it immediately appears again.

Any ideas? I thought it was a problem with the new contentOffset but it doesn't seem the case.

SaldaVonSchwartz
  • 3,769
  • 2
  • 41
  • 78

1 Answers1

0

The only workaround I've found so far is to tell the collection view to reload the items I'm making sticky, right after I do the reloadData. That is, considering that from my sample code the layout object has a stickyIndexPaths array, I can do:

[self.collectionView reloadData];
[self.collectionView reloadItemsAtIndexPaths:self.layout.stickyIndexPaths];

(note that I keep a reference to the collection view's layout in my view controller).

This prevents the sticky cells from disappearing but I still don't really know why they are disappearing in the first place.

SaldaVonSchwartz
  • 3,769
  • 2
  • 41
  • 78