6

UITableView has methods for determining which cells are currently visible. What I'm trying to find out is how much of a cell is visible.

For instance, as you drag a table down the 'newly visible' cell at the top of the table doens't just appear but appears a (pixel) line at a time until the whole cell is visible. How can I tell how much of that cell is visible at any given moment as the table view is dragged.

My final aim is, as the user drags on the table, to change the appearing view within the cell dependent on how much of it is visible at any given time.

Any suggestions?

AJ.
  • 1,186
  • 1
  • 14
  • 19

5 Answers5

7

You can try something like this:

-(void)scrollViewDidScroll:(UIScrollView *)sender
{
    [self checkWhichVideoToEnable];
}

-(void)checkWhichVideoToEnable
{
    for(UITableViewCell *cell in [tblMessages visibleCells])
    {
        if([cell isKindOfClass:[VideoMessageCell class]])
        {
            NSIndexPath *indexPath = [tblMessages indexPathForCell:cell];
            CGRect cellRect = [tblMessages rectForRowAtIndexPath:indexPath];
            UIView *superview = tblMessages.superview;

            CGRect convertedRect=[tblMessages convertRect:cellRect toView:superview];
            CGRect intersect = CGRectIntersection(tblMessages.frame, convertedRect);
            float visibleHeight = CGRectGetHeight(intersect);

            if(visibleHeight>VIDEO_CELL_SIZE*0.6) // only if 60% of the cell is visible
            {
                // unmute the video if we can see at least half of the cell
                [((VideoMessageCell*)cell) muteVideo:!btnMuteVideos.selected];
            }
            else
            {
                // mute the other video cells that are not visible
                [((VideoMessageCell*)cell) muteVideo:YES];
            }
        }
    }
}
Catalin
  • 1,631
  • 4
  • 24
  • 31
6

Here is a variation of the above implemented in Swift:

    var cellRect = tableView.rectForRowAtIndexPath(indexPath)
    if let superview = tableView.superview {
        let convertedRect = tableView.convertRect(cellRect, toView:superview)
        let intersect = CGRectIntersection(tableView.frame, convertedRect)
        let visibleHeight = CGRectGetHeight(intersect)
    }

visibleHeight being the part of the cell that is visible. One further step can be added to calculate the ratio - between zero and one - of the cell that is visible:

    var cellRect = tableView.rectForRowAtIndexPath(indexPath)
    if let superview = tableView.superview {
        let convertedRect = tableView.convertRect(cellRect, toView:superview)
        let intersect = CGRectIntersection(tableView.frame, convertedRect)
        let visibleHeight = CGRectGetHeight(intersect)
        let cellHeight = CGRectGetHeight(cellRect)
        let ratio = visibleHeight / cellHeight
    }

To change view appearances depending on visibility - as the question states above - this code should be included in the table view's UIScrollView superclass delegate, UIScrollViewDelegate method scrollViewDidScroll.

However, that will only affect cells as they scroll. Cells already visible would not be affected. For those, the same code should be applied in the UITableViewDelegate method didEndDisplayingCell.

Max MacLeod
  • 24,338
  • 10
  • 91
  • 123
4

I haven't tested it, but i'd try something along the lines of:

UITableViewCell *cell;
UIView *parent = cell.superview;
CGRect overlap = CGRectIntersection(cell.frame, parent.bounds);

then compare the particular rectangles.

bshirley
  • 7,674
  • 1
  • 31
  • 38
2

I used bshirley's advice and some others to make something like this. Works like a charm for my stuff that only shows a couple of cells at a time. May need some tweaking if you want to use this with more cells

    - (void) scrollViewDidEndDecelerating:(UITableView *)scrollView {

NSArray *visibleCellIndexPaths = TableView.indexPathsForVisibleRows;
NSIndexPath *indexPathLow = [visibleCellIndexPaths valueForKeyPath:@"@min.self"];
NSIndexPath *indexPathHigh = [visibleCellIndexPaths valueForKeyPath:@"@max.self"];

NSArray *cells = TableView.visibleCells;

UITableViewCell *cell1 = [cells objectAtIndex:0];
UIView *parent1 = cell1.superview;
CGRect overlap1 = CGRectIntersection(cell1.frame, parent1.bounds);

UITableViewCell *cell2 = [cells objectAtIndex:1];
UIView *parent2 = cell2.superview;
CGRect overlap2 = CGRectIntersection(cell2.frame, parent2.bounds);

if (overlap1.size.height > overlap2.size.height) {
    [TableView scrollToRowAtIndexPath:indexPathLow atScrollPosition:UITableViewScrollPositionTop animated:YES];
} else {
    [TableView scrollToRowAtIndexPath:indexPathHigh atScrollPosition:UITableViewScrollPositionTop animated:YES];
}

}

ColossalChris
  • 5,078
  • 2
  • 34
  • 43
2

See answer by Vadim Yelagin here: https://stackoverflow.com/a/9843146/758298 which explains how to convert a cells rect into the parent table view coordinates and determine if it is fully visible or not.

Community
  • 1
  • 1
gamozzii
  • 3,871
  • 1
  • 27
  • 33