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
余談ですが、前の質問すべてに行き、あなたを助けた人の答えを受け入れる必要があります。
Baub

4
教えてくれてありがとう!私はすでに彼らに+1を与えていましたが、セット承認済み回答機能を忘れていました。
RohinNZ

あなたのコードは私には正しいように見え、複雑ですが、うまくいきます。壊れていないものを修正しませんか?
CodaFi 2012年

回答:


125

rectForRowAtIndexPath:メソッドを使用してセルの四角形を取得し、CGRectContainsRect関数を使用してそれをテーブルビューの境界四角形と比較できます。

これが表示されていない場合はセルをインスタンス化しないため、かなり高速になることに注意してください。

迅速

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

Obj-C

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

もちろん、これはテーブルビューがスーパービューによってクリップされたり、別のビューによって隠されたりすることを考慮しません。


ありがとうございました!このコードをlnafzigerのソリューションと組み合わせました。
RohinNZ

11
2番目の考えでは、それはちょうどある可能性がありますCGRectContainsRect(tableView.bounds, [tableView rectForRowAtIndexPath:indexPath])
Vadim Yelagin 2013年

1
なぜ私のcellRectのorigin.x = 0、origin.y = 0、width = 0、height = 0なのですか?UIではすべて0とは限りませんが、自動レイアウトを使用していますか?
RainCast

7
スウィフト3:let completelyVisible = tableView.bounds.contains(tableView.rectForRow(at: indexPath))
cbartel 2017

UICollectionViewの同様の機能をどのように処理できますか?
Satyam

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私はあなたの解決策を試しましたが、私のテーブルビューが遅くなります。スクロールのパフォーマンスを向上させる方法はありますか?
Rahul Vyas

@RahulVyas申し訳ありませんが、:(内部要素を確認してくださいレイヤー/サブレイヤーに関してレイアウトを少し最適化できるかもしれません
Catalin

5

ドキュメントから:

visibleCellsレシーバーに表示されるテーブルセルを返します。

- (NSArray *)visibleCells

戻り値UITableViewCellオブジェクトを含む配列。各オブジェクトは、受信するテーブルビューの表示セルを表します。

iOS 2.0以降で利用できます。

参照– indexPathsForVisibleRows


4
すみません、はっきりしていませんでした。完全に見える細胞だけに興味があります。-(NSArray *)visibleCellsとindexPathsForVisibleRowsは両方とも、完全に表示されているセルと部分的に表示されているセルを返します。あなたが私のコードで見ることができるように、私はすでに-(NSArray *)visibleCellsを使用して、完全に表示されているセルを見つけています。とにかくありがとう。
RohinNZ

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の次の関数をよりよく使用しました

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)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.