Wie Sie wissen, wann UITableView im iPhone nach unten gescrollt hat


100

Ich würde gerne wissen, wann a UITableViewnach unten gescrollt hat, um mehr Inhalt zu laden und anzuzeigen, so etwas wie ein Delegat oder etwas anderes, um den Controller wissen zu lassen, wann die Tabelle nach unten gescrollt hat.

Weiß jemand das, bitte helfen Sie mir, danke im Voraus!

Antworten:


18

Der beste Weg ist, einen Punkt am unteren Bildschirmrand zu testen und diesen Methodenaufruf zu verwenden, wenn der Benutzer einen Bildlauf durchführt ( scrollViewDidScroll):

- (NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point

Testen Sie einen Punkt am unteren Bildschirmrand und geben Sie dann mit dem zurückgegebenen indexPath an, ob dieser indexPath die letzte Zeile ist. Fügen Sie dann Zeilen hinzu, wenn dies der Fall ist.


Leider aktualisiert meine App die Daten für vorhandene Zeilen in dieser Tabelle in Echtzeit, sodass auf diese Weise möglicherweise mehr Inhalt erhalten wird, wenn die Tabelle überhaupt nicht gescrollt wird.
Sohn Nguyen

247

Machen Sie in der Tabellenansicht so etwas

Objekt:

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
    CGPoint offset = aScrollView.contentOffset;
    CGRect bounds = aScrollView.bounds;
    CGSize size = aScrollView.contentSize;
    UIEdgeInsets inset = aScrollView.contentInset;
    float y = offset.y + bounds.size.height - inset.bottom;
    float h = size.height;
    // NSLog(@"offset: %f", offset.y);   
    // NSLog(@"content.height: %f", size.height);   
    // NSLog(@"bounds.height: %f", bounds.size.height);   
    // NSLog(@"inset.top: %f", inset.top);   
    // NSLog(@"inset.bottom: %f", inset.bottom);   
    // NSLog(@"pos: %f of %f", y, h);

    float reload_distance = 10;
    if(y > h + reload_distance) {
        NSLog(@"load more rows");
    }
}

Schnell:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let offset = scrollView.contentOffset
    let bounds = scrollView.bounds
    let size = scrollView.contentSize
    let inset = scrollView.contentInset
    let y = offset.y + bounds.size.height - inset.bottom
    let h = size.height
    let reload_distance:CGFloat = 10.0
    if y > (h + reload_distance) {
        print("load more rows")
    }
}

Gut! Verwenden Sie einfach: y> = h + reload_distance, was eigentlich nur dann ein Problem ist, wenn reload_distance = 0 ist (z. B. für Bounces NO).
Chris Prince

4
Dieser Code in "scrollViewDidScroll" wird immer dann ausgeführt, wenn der Benutzer seinen Finger auf dem Bildschirm nach oben / unten bewegt. Es ist besser, es unter "scrollViewDidEndDecelerating" zu verschieben. Auf diese Weise wird es einmal ausgeführt, wenn der Benutzer seinen Finger nicht mehr bewegt.
Mischa

hmm .. funktioniert dieser code noch? Ich bekomme nicht den Grund für UITableView. Ich wünschte, ich wüsste die
Gründe für

Offset: 237.000000 content.height: 855.000000 bounds.height: 667.000000 inset.top: 64.000000 inset.bottom: 49.000000 pos: 855.000000 von 855.000000 wenn falsch wird
Abhishek Thapliyal

62

Modifizierte Neoneyes antworten ein bisschen.

Diese Antwort richtet sich an diejenigen unter Ihnen, die nur einmal pro Fingerfreigabe das Ereignis auslösen möchten .

Geeignet, wenn mehr Inhalte von einem Inhaltsanbieter (Webdienst, Kerndaten usw.) geladen werden. Beachten Sie, dass dieser Ansatz die Antwortzeit Ihres Webdienstes nicht berücksichtigt.

- (void)scrollViewDidEndDragging:(UIScrollView *)aScrollView
                  willDecelerate:(BOOL)decelerate
{
    CGPoint offset = aScrollView.contentOffset;
    CGRect bounds = aScrollView.bounds;
    CGSize size = aScrollView.contentSize;
    UIEdgeInsets inset = aScrollView.contentInset;
    float y = offset.y + bounds.size.height - inset.bottom;
    float h = size.height;

    float reload_distance = 50;
    if(y > h + reload_distance) {
        NSLog(@"load more rows");
    }
}

Das funktioniert nicht. Immer wenn der Benutzer nach unten scrollt, wird "Mehr Zeilen laden" aufgerufen. Dies geschieht auch dann, wenn der Benutzer bereits nach unten gescrollt hat und darauf wartet, dass die API Daten zurückgibt.
Dean

2
Dean, bei der Frage ging es darum zu wissen, wann die Tabellenansicht nach unten gescrollt wurde. Ob Sie Daten von einer API abrufen oder nicht, spielt keine Rolle, oder?
Augapfel

Es wird jedoch keine Meldung angezeigt, in der mehr geladen wird, sodass der Benutzer erkennen kann, dass mehr Ergebnisse angezeigt werden müssen.
iPhone 7

Schöne Antwort in meinem Fall funktioniert es, wenn: float reload_distance = 10; if (y> (h - reload_distance)) {NSLog (@ "mehr Zeilen laden"); } weil y = 565 und h auch 565 ist
Abhishek Thapliyal

1
Ich habe sowohl die Antwort von Eyeball als auch von @neoneye ausprobiert. Ob Sie es glauben oder nicht, 'scrollViewDidScroll' wurde im Vergleich zu 'scrollViewDidEndDragging' mehrmals aufgerufen. Es war also effizient, 'scrollViewDidEndDragging' zu verwenden, da ich einen API-Aufruf darauf habe.
Vinod Supnekar

39

Fügen Sie diese Methode hinzu in UITableViewDelegate:

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{   
    CGFloat height = scrollView.frame.size.height;

    CGFloat contentYoffset = scrollView.contentOffset.y;

    CGFloat distanceFromBottom = scrollView.contentSize.height - contentYoffset;

    if(distanceFromBottom < height) 
    {
        NSLog(@"end of the table");
    }
}

3
Ich mag diese Lösung bis auf ein kleines Detail. if (distanceFromBottom <= height)
Mark McCorkle

Dies hat mir geholfen, mein Problem zu beheben, danke !! eine Tab-Leiste !! Es war eine blöde Tab-Leiste !!
Dmytro

Diese Arbeit ist wie ein Zauber, und es ist auch einfach, aber ich stehe vor einem kleinen Problem, als würde es zwei- oder dreimal in der letzten Reihe ausgeführt. Wie kann es nur einmal ausgeführt werden, wenn es die letzte Zeile der Tabellenansicht erreicht?
R. Mohan

Ich danke dir sehr!! Ich habe diesen Code in scrollViewWillBeginDecelerating eingefügt und er wird nur einmal ausgeführt, wenn zur letzten Zeile gescrollt wird.
Manali

20

Keine der obigen Antworten hat mir geholfen, also habe ich mir Folgendes ausgedacht:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)aScrollView
{   
    NSArray *visibleRows = [self.tableView visibleCells];
    UITableViewCell *lastVisibleCell = [visibleRows lastObject];
    NSIndexPath *path = [self.tableView indexPathForCell:lastVisibleCell];
    if(path.section == lastSection && path.row == lastRow)
    {
        // Do something here
    }
}

Können Sie erläutern, woher "lastSection" kommt?
Ainesophaur

Dies ist für die
Bildlaufansicht

19

Verwenden Sie – tableView:willDisplayCell:forRowAtIndexPath:( UITableViewDelegateMethode)

Vergleichen Sie das einfach indexPathmit den Elementen in Ihrem Datenarray (oder der Datenquelle, die Sie für Ihre Tabellenansicht verwenden), um herauszufinden, ob das letzte Element angezeigt wird.

Docs: http://developer.apple.com/library/ios/documentation/uikit/reference/UITableViewDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/UITableViewDelegate/tableView:willDisplayCell:forRowAtIndexPath :


Wenn Sie dann die Daten von tableView neu laden, stecken Sie in einer Endlosschleife fest.
Dielson Sales

14

UITableViewist eine Unterklasse von UIScrollViewund UITableViewDelegateentspricht UIScrollViewDelegate. Der Delegat, den Sie an die Tabellenansicht anhängen, erhält also Ereignisse wie scrollViewDidScroll:und Sie können Methoden wie contentOffsetin der Tabellenansicht aufrufen , um die Bildlaufposition zu ermitteln.


Ja, das ist es, wonach ich suche, indem ich diesen Delegaten implementiere und überprüfe, ob die Bildlaufposition UITableViewScrollPositionBottom ist. Ich werde wissen, wann die Tabelle nach unten gescrollt wird, vielen Dank
Son Nguyen

8
NSLog(@"%f / %f",tableView.contentOffset.y, tableView.contentSize.height - tableView.frame.size.height);

if (tableView.contentOffset.y == tableView.contentSize.height - tableView.frame.size.height)
        [self doSomething];

Schön und einfach


8

in Swiftkann man so etwas machen. Die folgende Bedingung gilt jedes Mal, wenn Sie das Ende der Tabellenansicht erreichen

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
        if indexPath.row+1 == postArray.count {
            println("came to last row")
        }
}

1
Das Problem ist, wenn die Tabellenansicht nur 3 Zellen hat, zum Beispiel, dass println("came to last row")dieser Code ausgeführt wird!
Mc.Lover

@ Mc.Lover das war genau das Problem für mich, für das die CPU-Auslastung auf 92% ging. neoneyes antwort hat bei mir funktioniert. Ich habe auch eine schnelle Version hinzugefügt, wenn Sie brauchen.
Anuran Barman

7

Aufbauend auf der Antwort von @Jay Mayu, die ich für eine der besseren Lösungen hielt:

Ziel c

// UITableViewDelegate
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {

// Need to call the service & update the array
if(indexPath.row + 1 == self.sourceArray.count) {
    DLog(@"Displayed the last row!");
  }
}

Swift 2.x.

// UITableViewDelegate
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    if (indexPath.row + 1) == sourceArray.count {
        print("Displayed the last row!")
    }
}

5

Ich benutze dies im Allgemeinen, um mehr Daten zu laden, wenn die Anzeige der letzten Zelle beginnt

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
   if (indexPath.row ==  myDataArray.count-1) {
        NSLog(@"load more");
    }
}

1
das ist falsch. Jedes Mal, wenn Sie reloadData aufrufen, wird eine Anfrage gestellt. Und manchmal müssen Sie nur die Tabelle neu laden.
Patrick Bassut

Sie wollten sagen, wenn Sie in der letzten Zelle sind und dann Daten neu laden, dann wird dies passieren?
Shaik Riyaz

5

Nehmen Sie neoneye ausgezeichnete Antworten, aber schnell, und benennen Sie die Variablen um.

Grundsätzlich wissen wir, dass wir den unteren Rand der Tabellenansicht erreicht haben, wenn der yOffsetAtBottomWert über der Höhe des Tabelleninhalts liegt.

func isTableViewScrolledToBottom() -> Bool {
    let tableHeight = tableView.bounds.size.height
    let contentHeight = tableView.contentSize.height
    let insetHeight = tableView.contentInset.bottom

    let yOffset = tableView.contentOffset.y
    let yOffsetAtBottom = yOffset + tableHeight - insetHeight

    return yOffsetAtBottom > contentHeight
}

5

Hier ist der schnelle 3.0-Versionscode.

   func scrollViewDidScroll(_ scrollView: UIScrollView) {

        let offset = scrollView.contentOffset
        let bounds = scrollView.bounds
        let size = scrollView.contentSize
        let inset = scrollView.contentInset
        let y: Float = Float(offset.y) + Float(bounds.size.height) + Float(inset.bottom)
        let height: Float = Float(size.height)
        let distance: Float = 10

        if y > height + distance {
            // load more data
        }
    }

3

Meine Lösung besteht darin, Zellen hinzuzufügen, bevor die Tabellenansicht beim geschätzten Versatz abgebremst wird. Es ist vorhersehbar durch die Geschwindigkeit.

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)offset {

    NSLog(@"offset: %f", offset->y+scrollView.frame.size.height);
    NSLog(@"Scroll view content size: %f", scrollView.contentSize.height);

    if (offset->y+scrollView.frame.size.height > scrollView.contentSize.height - 300) {
        NSLog(@"Load new rows when reaches 300px before end of content");
        [[DataManager shared] fetchRecordsNextPage];
    }
}

3

Update für Swift 3

Neoneyes Antwort hat in Ziel C am besten für mich funktioniert. Dies entspricht der Antwort in Swift 3:

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    let offset: CGPoint = scrollView.contentOffset
    let bounds: CGRect = scrollView.bounds
    let size: CGSize = scrollView.contentSize
    let inset: UIEdgeInsets = scrollView.contentInset
    let y: CGFloat = offset.y + bounds.size.height - inset.bottom
    let h: CGFloat = size.height
//        print("offset: %f", offset.y)
//        print("content.height: %f", size.height)
//        print("bounds.height: %f", bounds.size.height)
//        print("inset.top: %f", inset.top)
//        print("inset.bottom: %f", inset.bottom)
//        print("position: %f of %f", y, h)

    let reloadDistance: CGFloat = 10

    if (y > h + reloadDistance) {
            print("load more rows")
    }
}

2

Ich möchte eine Aktion für meine 1 vollständige Tableview-Zelle ausführen.

Der Code ist also Link:

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    NSArray* cells = self.tableView.visibleCells;
    NSIndexPath *indexPath = nil ;

    for (int aIntCount = 0; aIntCount < [cells count]; aIntCount++)
    {

        UITableViewCell *cell = [cells objectAtIndex:aIntCount];
CGRect cellRect = [self.tableView convertRect:cell.frame toView:self.tableView.superview];

        if (CGRectContainsRect(self.tableView.frame, cellRect))
        {
            indexPath = [self.tableView indexPathForCell:cell];

    //  remain logic
        }
     }
}

Möge dies jemandem helfen.


0

@ neoneyes Antwort hat bei mir funktioniert. Hier ist die Swift 4- Version davon

    let offset = scrollView.contentOffset
    let bounds = scrollView.bounds
    let size = scrollView.contentSize
    let insets = scrollView.contentInset
    let y = offset.y + bounds.size.height - insets.bottom
    let h = size.height
    let reloadDistance = CGFloat(10)
    if y > h + reloadDistance {
        //load more rows
    }
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.