Am besten überprüfen Sie, ob UITableViewCell vollständig sichtbar ist


100

Ich habe eine UITableView mit Zellen unterschiedlicher Höhe und muss wissen, ob sie vollständig sichtbar sind oder nicht.

Im Moment durchlaufe ich jede Zelle in der Liste der sichtbaren Zellen, um zu überprüfen, ob sie jedes Mal, wenn die Ansicht gescrollt wird, vollständig sichtbar ist. Ist das der beste Ansatz?

Hier ist mein Code:

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

Bearbeiten:

Bitte beachten Sie, dass * - (NSArray ) visibleCells sichtbare Zellen zurückgibt, die sowohl vollständig als auch teilweise sichtbar sind.

Bearbeiten 2:

Dies ist der überarbeitete Code, nachdem Lösungen von lnafziger und Vadim Yelagin kombiniert wurden :

- (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
Nur als Randnotiz sollten Sie zu all Ihren vorherigen Fragen gehen und Antworten von denen akzeptieren, die Ihnen geholfen haben.
Baub

4
Danke, dass du es mir gesagt hast! Ich hatte ihnen bereits +1 gegeben, aber die eingestellte Antwortfunktion vergessen.
RohinNZ

Ihr Code sieht für mich korrekt aus, und obwohl er kompliziert ist, funktioniert er. Repariere nicht, was nicht kaputt ist, oder?
CodaFi

Antworten:


125

Sie können das Rechteck einer Zelle mit rectForRowAtIndexPath:method abrufen und es mit dem Rechteck von tableview vergleichenCGRectContainsRect Funktion Funktion .

Beachten Sie, dass dies die Zelle nicht instanziiert, wenn sie nicht sichtbar ist, und daher ziemlich schnell ist.

Schnell

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

Obj-C

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

Dies berücksichtigt natürlich nicht, dass die Tabellenansicht von einer Übersicht abgeschnitten oder von einer anderen Ansicht verdeckt wird.


Danke dir! Ich habe diesen Code mit der Lösung von lnafziger kombiniert.
RohinNZ

11
Beim zweiten Gedanken kann es nur seinCGRectContainsRect(tableView.bounds, [tableView rectForRowAtIndexPath:indexPath])
Vadim Yelagin

1
Warum ist das der Ursprung meines cellRect.x = 0, der Ursprung.y = 0, die Breite = 0, die Höhe = 0? Obwohl auf der Benutzeroberfläche nicht alle Nullen sind, verwende ich das automatische Layout. Irgendwelche Ideen?
RainCast

7
Swift 3:let completelyVisible = tableView.bounds.contains(tableView.rectForRow(at: indexPath))
Cbartel

Wie können wir mit ähnlichen Funktionen für UICollectionView umgehen?
Satyam

64

Ich würde es so ändern:

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

Das <und> in der if-Anweisung sollte <= oder> = sein, ich werde dies in der Antwort korrigieren ...
Aardvark

12

Sie können so etwas versuchen, um zu sehen, wie viel Prozent sichtbar sind:

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

Wenn in der Tabellenansicht 2 Zellen mit Videos angezeigt werden können (z. B. auf dem iPad), werden beide Videos mit dem obigen Code abgespielt.
Jerome

@Catalin Ich habe Ihre Lösung ausprobiert, aber meine Tabellenansicht wird langsamer. Gibt es eine Möglichkeit, die Bildlaufleistung zu verbessern?
Rahul Vyas

@ RahulVyas sorry aber nein :( Überprüfen Sie Ihre inneren Elemente, vielleicht kann das Layout ein wenig optimiert werden, was Ebenen / Unterschichten betrifft
Catalin

5

Aus den Dokumenten:

sichtbare Zellen Gibt die Tabellenzellen zurück, die im Empfänger sichtbar sind.

- (NSArray *)visibleCells

Rückgabewert Ein Array mit UITableViewCell-Objekten, die jeweils eine sichtbare Zelle in der empfangenden Tabellenansicht darstellen.

Verfügbarkeit Verfügbar in iOS 2.0 und höher.

Siehe auch - indexPathsForVisibleRows


4
Entschuldigung, vielleicht war ich nicht klar genug. Ich interessiere mich nur für die Zellen, die vollständig sichtbar sind. - (NSArray *) visibleCells und indexPathsForVisibleRows geben beide die Zellen zurück, die vollständig sichtbar und teilweise sichtbar sind. Wie Sie in meinem Code sehen können, verwende ich bereits - (NSArray *) visibleCells, um diejenigen zu finden, die vollständig sichtbar sind. Trotzdem danke.
RohinNZ

4

Wenn Sie auch das contentInset berücksichtigen und sich nicht auf eine Übersicht verlassen möchten (der Tabellenansichtsrahmen in der Übersicht könnte etwas anderes als 0,0 sein), ist hier meine Lösung:

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)
    }
}

1
- (BOOL)checkVisibilityOfCell{
    if (tableView.contentSize.height <= tableView.frame.size.height) {
        return YES;
    } else{
        return NO;
    }
}

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

Auch wenn Sie gesagt haben, dass Sie es jedes Mal überprüfen möchten, wenn Sie gescrollt haben, können Sie es auch verwenden

-(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

Vielleicht wird für dieses Problem die nächste Funktion von UITableViewDelegate besser verwendet

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

Dies wird nicht funktionieren. Aus einem doctells the delegate that the specified cell was removed from the table.
anatoliy_v

0

Mit dem folgenden Code können Sie überprüfen, ob eine Sammlungsansichtszelle über die Layoutattribute der Sammlungsansicht vollständig sichtbar ist.

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.