Iván Peralta
Iván Peralta

Reputation: 851

UITableView with UICollectionView in row weird scroll in different rows at the same time

We have a UITableView, every row as a UICollectionView that supports an horizontal scroll, without paging enabled.

We has the cells registered for reuse,

    // Setup for VenueFilterTableViewCell
    NSString * cellIdentifier = @"VenueFilterTableViewCell";
    VenueFilterTableViewCell *tbCell = [self.tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if (tbCell == nil) {
        tbCell = [[VenueFilterTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                                 reuseIdentifier:cellIdentifier];
        tbCell.selectionStyle = UITableViewCellSelectionStyleNone;
    }
    // Method for inflate the UICollectionView in the row
    [tbCell setVenues:venues
              loading:NO
           atLocation:self.definedLocation
     forFilter:filter
         withDelegate:self];
    cell = tbCell;

When I scroll the row horizontally the UICollectionView at the indexPath.row 0 in the UITableViewCell, the indexPath.row 3 (initially out of the screen) scroll at the same time. So, if after scroll horizontal, you move scroll down quickly you can see the row 3, and the row 7... and so on, scrolling at the same time.

I have a progress bar in each cell, for providing feedback to the user how far to the end of the horizontal scroll he is, but because of this reuse behaviour, each row involved (0 and 3 and 7) is messing up the progress of the other.

Any suggestions?

UPDATE

I added into the UITableView the next event for controlling when the cell is out of the screen and force the UICollectionView inside to stop scrolling. That enhanced a bit the performance, but eventually the issue happen again.

Code in the UITableViewController for detecting when the row is out of the screen:

- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    if ([tableView.indexPathsForVisibleRows indexOfObject:indexPath] == NSNotFound) {
        // This indeed is an indexPath no longer visible
        // Do something to this non-visible cell...
        if ([cell isKindOfClass:[VenueFilterTableViewCell class]]) {
            VenueFilterTableViewCell *tbCell = (VenueFilterTableViewCell *) cell;
            [tbCell stopScrolling];
        }
    }
}

Code in the UITableViewCell with the UICollection View, in the reload content, apart from recovering the contentOffset, we need to re-enable the self.collectionView.scrollEnabled = YES;

- (void) stopScrolling {
    self.collectionView.scrollEnabled = NO;
    [self.collectionView setContentOffset:self.collectionView.contentOffset animated:NO];
}

Upvotes: 2

Views: 424

Answers (2)

Prashant Tukadiya
Prashant Tukadiya

Reputation: 16416

My first answer was not helpful. so I have fixed in this one Sorry to put it in swift you can easily convert that in objc

I have implemented willDisplayCell and endDispayCell with following code

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    guard let cell = cell as? ScreenShotsCell else { return }
    self.configureCell(for: indexPath, to: cell)
    cell.collectionView.setContentOffset(cell.collectionView.contentOffset, animated: true)
    cell.collectionViewOffset = storedOffsets[indexPath.row] ?? 0
}

//--------------------------------------------------------------------------------

func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    guard let cell = cell as? ScreenShotsCell else { return }
    storedOffsets[indexPath.row] = cell.collectionViewOffset
    cell.collectionView.setContentOffset(cell.collectionView.contentOffset, animated: true)
}

Main core logic is cell.collectionView.setContentOffset so it will stop scroll when it is not visible so it will fix the issue of other row's of tableview scrolling because of reuse cell

Hope it is helpful

Upvotes: 1

Jakub Truhlář
Jakub Truhlář

Reputation: 20710

Seems like a typical issue caused by cell reusing. If you really have a UICollectionView as a part of a cell, the best way is to create a Dictionary of stored offsets.

Quick example:

var storedOffsets: [Int: CGFloat] = [:]

in tableView:didEndDisplaying:forRowAt: store the offsets of a collectionView's contentOffset.

and

then in tableView:willDisplay:forRowAt: use these stored offsets to set the contentOffset of the collectionView in the cell.

Upvotes: 0

Related Questions