Jak się dowiedzieć, kiedy UITableView przewinął się do dołu w iPhonie

100

Chciałbym wiedzieć, kiedy UITableViewzrobiono przewinięcie do dołu, aby załadować i wyświetlić więcej treści, coś w rodzaju delegata lub coś innego, aby powiadomić kontroler, kiedy tabela przewinęła się do dołu.

Czy ktoś o tym wie, pomóż mi, z góry dziękuję!

Son Nguyen
źródło

Odpowiedzi:

18

Najlepszym sposobem jest przetestowanie punktu na dole ekranu i użycie tej metody za każdym razem, gdy użytkownik przewinie ( scrollViewDidScroll):

- (NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point

Przetestuj punkt w dolnej części ekranu, a następnie za pomocą indexPath zwraca sprawdzenie, czy ten indexPath jest ostatnim wierszem, a jeśli tak, dodaj wiersze.

Ajay
źródło
Niestety, moja aplikacja będzie aktualizować dane w czasie rzeczywistym dla istniejących wierszy w tej tabeli, więc w ten sposób może uzyskać więcej treści, gdy tabela nie jest w ogóle przewijana.
Son Nguyen
247

w delegacie tableview zrób coś takiego

CelC:

- (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");
    }
}

Szybki:

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")
    }
}
neoneye
źródło
Dobry! Po prostu użyj: y> = h + reload_distance, co jest tak naprawdę problemem tylko wtedy, gdy reload_distance = 0 (np. Dla odskoków NIE).
Chris Prince,
4
Ten kod w "scrollViewDidScroll" jest wykonywany przez cały czas, gdy użytkownik przesuwa palcem w górę / w dół ekranu. Lepiej jest przenieść go pod „scrollViewDidEndDecelerating”. W ten sposób zostanie wykonany raz, gdy użytkownik przestanie poruszać palcem.
Misha
hmm .. ten kod nadal działa? nie dostaję dna UITableView dla mnie. Chciałbym znać
powód
offset: 237.000000 content.height: 855.000000 bound.height: 667.000000 inset.top: 64.000000 inset.bottom: 49.000000 pos: 855.000000 z 855.000000 jeśli robi się false
Abhishek Thapliyal
62

Zmodyfikowane neoneyes odpowiadają trochę.

Ta odpowiedź jest skierowana do tych z Was, którzy chcą, aby zdarzenie było uruchamiane tylko raz na każde puszczenie palca .

Odpowiednie w przypadku ładowania większej ilości treści od niektórych dostawców treści (usługa internetowa, podstawowe dane itp.). Zwróć uwagę, że to podejście nie uwzględnia czasu odpowiedzi z Twojej usługi internetowej.

- (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");
    }
}
Gałka oczna
źródło
To nie działa. Za każdym razem, gdy użytkownik przewinie w dół, wywoływane jest polecenie „załaduj więcej wierszy”. Dzieje się tak nawet wtedy, gdy użytkownik przewinął już do dołu i czeka, aż API zwróci dane.
Dziekan
2
Dean, pytanie dotyczyło ustalenia, kiedy widok tabeli przewija się na dół. To, czy pobierasz dane z interfejsu API, czy nie, jest nieistotne, prawda?
Gałka oczna
Ale nie ma komunikatu pokazującego, że załaduj więcej ... aby użytkownik wiedział, że jest więcej wyników do wyświetlenia.
iPhone 7
Dobra odpowiedź w moim przypadku działa, jeśli: float reload_distance = 10; if (y> (h - reload_distance)) {NSLog (@ "załaduj więcej wierszy"); } ponieważ y = 565, a h to również 565
Abhishek Thapliyal
1
Wypróbowałem zarówno odpowiedź Eyeball, jak i @neoneye. Wierz mi lub nie, „scrollViewDidScroll” wywoływana wielokrotnie w porównaniu do „scrollViewDidEndDragging”. Dlatego efektywne było użycie „scrollViewDidEndDragging”, ponieważ mam wywołanie API.
Vinod Supnekar
39

dodaj tę metodę w 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");
    }
}
8suhas
źródło
3
Podoba mi się to rozwiązanie poza jednym małym szczegółem. if (distanceFromBottom <= height)
Mark McCorkle
To pomogło mi rozwiązać mój problem, dziękuję !! pasek kart !! to był głupi pasek z zakładkami !!
Dmytro
to działa jak urok i jest też proste, ale mam do czynienia z jednym małym problemem, takim jak wykonywanie dwóch lub trzech razy w ostatnim rzędzie. jak sprawić, by był wykonywany tylko raz, gdy dotrze do ostatniego wiersza widoku tabeli?
R. Mohan
Dziękuję bardzo!! Umieściłem ten kod w scrollViewWillBeginDecelerating i jest on wykonywany tylko raz po przewinięciu do ostatniego wiersza.
Manali,
20

Żadna z powyższych odpowiedzi nie pomogła mi, więc wymyśliłem to:

- (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
    }
}
Iftach Orr
źródło
Czy możesz wyjaśnić, skąd pochodzi „lastSection”?
ainesophaur
to jest dla widoku przewijania, a nie tabeli
iosMentalist
19

Użyj – tableView:willDisplayCell:forRowAtIndexPath:( UITableViewDelegatemetoda)

Po prostu porównaj element indexPathz elementami w tablicy danych (lub jakimkolwiek źródłem danych, którego używasz w widoku tabeli), aby dowiedzieć się, czy ostatni element jest wyświetlany.

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

Wolfgang Schreurs
źródło
Następnie, jeśli ponownie załadujesz dane tableView, utkniesz w nieskończonej pętli.
Sprzedaż Dielson
14

UITableViewjest podklasą UIScrollViewi UITableViewDelegatejest zgodna z UIScrollViewDelegate. Dlatego pełnomocnik dołączony do widoku tabeli otrzyma zdarzenia, takie jak scrollViewDidScroll:, i możesz wywołać metody, takie jak contentOffsetw widoku tabeli, w celu znalezienia pozycji przewijania.

Dezorganizacja
źródło
Tak, właśnie tego szukam, implementując tego delegata i sprawdzając, czy pozycja przewijania to UITableViewScrollPositionBottom, będę wiedzieć, kiedy tabela jest przewijana na dół, wielkie dzięki
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];

Ładnie i prosto

user1641587
źródło
8

w Swiftmożesz zrobić coś takiego. Następujący warunek będzie spełniony za każdym razem, gdy dojdziesz do końca widoku tabeli

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
        if indexPath.row+1 == postArray.count {
            println("came to last row")
        }
}
Jay Mayu
źródło
1
Problem polega na tym, że widok tabeli ma tylko 3 komórki, na przykład println("came to last row")ten kod działa!
Mc.Lover
@ Mc.Lover to był dla mnie dokładny problem, dla którego użycie procesora sięgało 92%. odpowiedź Neoneye zadziałała dla mnie. Dodałem również szybką wersję, jeśli potrzebujesz.
Anuran Barman
7

Opierając się na odpowiedzi @Jay Mayu, która moim zdaniem była jednym z lepszych rozwiązań:

Cel 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!")
    }
}
footyapps27
źródło
5

Generalnie używam tego do załadowania większej ilości danych, kiedy ostatnia komórka zaczyna wyświetlać

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
   if (indexPath.row ==  myDataArray.count-1) {
        NSLog(@"load more");
    }
}
Shaik Riyaz
źródło
1
To jest źle. Za każdym razem, gdy dzwonisz do reloadData, zostanie wysłane żądanie. Czasami wystarczy ponownie załadować stół.
Patrick Bassut
chciałeś powiedzieć, jeśli twoja w ostatniej komórce, a następnie dokonałeś ponownego załadowania danych, to tak się stanie?
Shaik Riyaz
5

Przyjmowanie doskonałych odpowiedzi Neoneye, ale szybko i zmiana nazw zmiennych.

Zasadniczo wiemy, że dotarliśmy do dołu widoku tabeli, jeśli wysokość yOffsetAtBottomjest większa niż wysokość zawartości tabeli.

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
}
samwize
źródło
5

Oto kod wersji Swift 3.0.

   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
        }
    }
Morshed Alam
źródło
3

Moim rozwiązaniem jest dodanie komórek przed spowolnieniem widoku tabeli przy szacowanym przesunięciu. Można to przewidzieć dzięki prędkości.

- (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];
    }
}
jodła
źródło
3

Aktualizacja dla Swift 3

Odpowiedź Neoneye zadziałała najlepiej w przypadku Celu C, jest to odpowiednik odpowiedzi w 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")
    }
}
Ibrahim
źródło
2

Chcę wykonać jakąś akcję na mojej dowolnej 1 pełnej komórce Tableviewcell.

Więc kod to 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
        }
     }
}

Niech to komuś pomoże.

Mayuri R Talaviya
źródło
0

Odpowiedź @ neoneye zadziałała dla mnie. Oto wersja Swift 4

    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
    }
Anuran Barman
źródło