检查UITableViewCell是否完全可见的最佳方法


100

我有一个带有不同高度的单元格的UITableView,我需要知道它们何时完全可见。

现在,我遍历可见单元列表中的每个单元,以检查每次滚动视图时是否完全可见。这是最好的方法吗?

这是我的代码:

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {

    CGPoint offset = aScrollView.contentOffset;
    CGRect bounds = aScrollView.bounds;    
    NSArray* cells = myTableView.visibleCells;

    for (MyCustomUITableViewCell* cell in cells) {

        if (cell.frame.origin.y > offset.y &&
            cell.frame.origin.y + cell.frame.size.height < offset.y + bounds.size.height) {

            [cell notifyCompletelyVisible];
        }
        else {

            [cell notifyNotCompletelyVisible];
        }
    }
}

编辑:

请注意,*-(NSArray )visibleCells返回的可见单元格既完全可见又部分可见。

编辑2:

这是结合了lnafzigerVadim Yelagin的解决方案之后的修订代码:

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
    NSArray* cells = myTableView.visibleCells;
    NSArray* indexPaths = myTableView.indexPathsForVisibleRows;

    NSUInteger cellCount = [cells count];

    if (cellCount == 0) return;

    // Check the visibility of the first cell
    [self checkVisibilityOfCell:[cells objectAtIndex:0] forIndexPath:[indexPaths objectAtIndex:0]];

    if (cellCount == 1) return;

    // Check the visibility of the last cell
    [self checkVisibilityOfCell:[cells lastObject] forIndexPath:[indexPaths lastObject]];

    if (cellCount == 2) return;

    // All of the rest of the cells are visible: Loop through the 2nd through n-1 cells
    for (NSUInteger i = 1; i < cellCount - 1; i++)
        [[cells objectAtIndex:i] notifyCellVisibleWithIsCompletelyVisible:YES];
}

- (void)checkVisibilityOfCell:(MultiQuestionTableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath {
    CGRect cellRect = [myTableView rectForRowAtIndexPath:indexPath];
    cellRect = [myTableView convertRect:cellRect toView:myTableView.superview];
    BOOL completelyVisible = CGRectContainsRect(myTableView.frame, cellRect);

    [cell notifyCellVisibleWithIsCompletelyVisible:completelyVisible];
}

3
只是附带说明,您应该阅读所有之前的问题,并接受帮助您的人的回答。
鲍勃2012年

4
谢谢你告诉我!我已经给他们+1了,但是却忘记了设定的可接受答案功能。
RohinNZ 2012年

您的代码对我来说看起来是正确的,尽管很复杂,但是可以工作。不修复没有坏的东西,是吗?
CodaFi 2012年

Answers:


125

您可以使用rectForRowAtIndexPath:method 获取单元格的rect,并使用CGRectContainsRectfunction 将其与tableview的rect进行比较。

请注意,如果该单元格不可见,它将不会实例化该单元格,因此将相当快。

迅速

let cellRect = tableView.rectForRowAtIndexPath(indexPath)
let completelyVisible = tableView.bounds.contains(cellRect)

对象

CGRect cellRect = [tableView rectForRowAtIndexPath:indexPath];
BOOL completelyVisible = CGRectContainsRect(tableView.bounds, cellRect);

当然,这不会考虑到表视图被超级视图剪切或被另一个视图遮挡。


谢谢!我已将此代码与lnafziger的解决方案结合在一起。
RohinNZ2012年

11
第二个想法可能只是CGRectContainsRect(tableView.bounds, [tableView rectForRowAtIndexPath:indexPath])
Vadim Yelagin

1
为什么我的cellRect的origin.x = 0,origin.y = 0,宽度= 0,高度= 0?尽管在UI上它们并非全为0,但我正在使用自动布局,有什么想法吗?
RainCast

7
斯威夫特3:let completelyVisible = tableView.bounds.contains(tableView.rectForRow(at: indexPath))
cbartel

我们如何处理UICollectionView的类似功能?
萨蒂扬

64

我会这样更改:

- (void)checkVisibilityOfCell:(MyCustomUITableViewCell *)cell inScrollView:(UIScrollView *)aScrollView {
    CGRect cellRect = [aScrollView convertRect:cell.frame toView:aScrollView.superview];

    if (CGRectContainsRect(aScrollView.frame, cellRect))
        [cell notifyCompletelyVisible];
    else
        [cell notifyNotCompletelyVisible];
}

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView { 
    NSArray* cells = myTableView.visibleCells;

    NSUInteger cellCount = [cells count];
    if (cellCount == 0)
        return;

    // Check the visibility of the first cell
    [self checkVisibilityOfCell:[cells firstObject] inScrollView:aScrollView];
    if (cellCount == 1)
        return;

    // Check the visibility of the last cell
    [self checkVisibilityOfCell:[cells lastObject] inScrollView:aScrollView];
    if (cellCount == 2)
        return;

    // All of the rest of the cells are visible: Loop through the 2nd through n-1 cells
    for (NSUInteger i = 1; i < cellCount - 1; i++)
        [[cells objectAtIndex:i] notifyCompletelyVisible];
}

if语句中的<和>应该为<=或> =,我将在答案中对此进行更正...
Aardvark

12

您可以尝试执行以下操作来查看可见的百分比:

-(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];
            }
        }
    }
}

如果表格视图能够显示2个单元格的视频(例如,在iPad上),则两个视频都将使用上述代码播放?
杰罗姆

@ Catalin我尝试了您的解决方案,但我的tableview变慢了。有什么方法可以改善滚动性能吗?
拉胡尔·维斯

@RahulVyas抱歉,但没有:(检查你的内部元素,也许因为它认为层/子层的布局可以优化一点
克特林

5

从文档:

visibleCells返回在接收器中可见的表单元格。

- (NSArray *)visibleCells

返回值包含UITableViewCell对象的数组,每个对象代表接收表视图中的可见单元格。

可用性在iOS 2.0和更高版本中可用。

另请参见– indexPathsForVisibleRows


4
抱歉,我还不够清楚。我只对完全可见的单元格感兴趣。-(NSArray *)visibleCells和indexPathsForVisibleRows都返回完全可见和部分可见的单元格。如您在我的代码中看到的,我已经在使用-(NSArray *)visibleCells来查找完全可见的那些。无论如何还是谢谢。
RohinNZ 2012年

4

如果您还想将contentInset考虑在内,并且不想依赖于一个超级视图(超级视图中的表视图框架可能不是0,0的东西),这是我的解决方案:

extension UITableView {

    public var boundsWithoutInset: CGRect {
        var boundsWithoutInset = bounds
        boundsWithoutInset.origin.y += contentInset.top
        boundsWithoutInset.size.height -= contentInset.top + contentInset.bottom
        return boundsWithoutInset
    }

    public func isRowCompletelyVisible(at indexPath: IndexPath) -> Bool {
        let rect = rectForRow(at: indexPath)
        return boundsWithoutInset.contains(rect)
    }
}


0
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
CGRect frame = cell.frame;
if (CGRectContainsRect(CGRectOffset(self.collectionView.frame, self.collectionView.contentOffset.x, self.collectionView.contentOffset.y), frame))
{
    // is on screen
}

0

即使您说每次滚动时都想检查它,也可以使用

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
       CGRect cellRect = [tableView convertRect:cell.frame toView:tableView.superview];
       if (CGRectContainsRect(tableView.frame, cellRect)){
            //Do things in case cell is fully displayed
        }

}

0

也许对此问题更好地使用了UITableViewDelegate的next函数

func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath)

这是行不通的。摘自文件tells the delegate that the specified cell was removed from the table.
anatoliy_v

0

下面的代码可让您检查集合视图单元格是否通过集合视图的布局属性完全可见。

guard let cellRect = collectionView.layoutAttributesForItem(at: indexPath)?.frame else { return } let isCellCompletelyVisible = collectionView.bounds.contains(cellRect)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.