Otrzymywać powiadomienia, gdy UITableView zakończy proszenie o dane?

108

Czy istnieje sposób, aby dowiedzieć się, kiedy UITableViewzakończyło się proszenie o dane ze swojego źródła danych?

Żadna z metod viewDidLoad/ viewWillAppear/ viewDidAppearskojarzonego kontrolera widoku ( UITableViewController) nie jest tutaj przydatna, ponieważ wszystkie uruchamiają się zbyt wcześnie. Żadne z nich (całkowicie zrozumiałe) nie gwarantuje, że zapytania do źródła danych na razie się zakończyły (np. Do przewinięcia widoku).

Jeden obejście znalazłem jest wywołanie reloadDataw viewDidAppear, ponieważ, gdy reloadDatapowróci, widok tabeli jest gwarancją zakończeniu kwerendy źródło danych tyle, ile potrzebuje na razie.

Wydaje się to jednak dość nieprzyjemne, ponieważ zakładam, że powoduje to dwukrotne pytanie źródła danych o te same informacje (raz automatycznie i raz z powodu reloadDatapołączenia) podczas pierwszego ładowania.

Powodem, dla którego chcę to w ogóle zrobić, jest to, że chcę zachować pozycję przewijania UITableView- ale aż do poziomu pikseli, a nie tylko do najbliższego wiersza.

Podczas przywracania pozycję przewijania (za pomocą scrollRectToVisible:animated:), muszę widoku tabeli do już wystarczające dane w nim, albo też scrollRectToVisible:animated:wywołanie metody nie robi nic (co jest to, co się dzieje, jeśli nawiązać połączenie na własną rękę w dowolnym viewDidLoad, viewWillAppearlub viewDidAppear).

kennethmac2000
źródło
Wygląda na to, że szukasz czegoś podobnego do tego stackoverflow.com/a/11672379/2082172 .
Timur Kuchkarov
Z mojego doświadczenia wynika, że ​​UITableView może buforować wywołania reloadData, tak jak buforuje wywołania do wstawiania / usuwania wierszy i innych rzeczy. UITableView wywoła delegata źródła danych, gdy będzie gotowy, w zależności od użycia procesora i innych rzeczy.
Walt Sellers,

Odpowiedzi:

61

Wydaje się, że ta odpowiedź już nie działa, z powodu pewnych zmian wprowadzonych w implementacji UITableView od czasu napisania odpowiedzi. Zobacz ten komentarz: Chcesz otrzymywać powiadomienia, gdy UITableView zakończy proszenie o dane?

Grałem z tym problemem przez kilka dni i myślę, że podklasy UITableView„s reloadDatajest najlepszym rozwiązaniem:

- (void)reloadData {

    NSLog(@"BEGIN reloadData");

    [super reloadData];

    NSLog(@"END reloadData");

}

reloadDatanie kończy się przed zakończeniem ponownego ładowania danych w tabeli. Tak więc, gdy drugi NSLogjest uruchamiany, widok tabeli w rzeczywistości zakończył żądanie danych.

Podklasowałem, UITableViewaby wysłać metody do delegata przed i po reloadData. To działa jak urok.

Eric MORAND
źródło
2
Muszę powiedzieć, że to wydaje się być najlepszym rozwiązaniem. Właśnie to wdrożyłem i, jak mówisz, działa jak urok. Dzięki!
teoria
2
@EricMORAND Mówisz, że „reloadData nie kończy się przed zakończeniem ponownego ładowania danych w tabeli”. Czy możesz wyjaśnić, co przez to rozumiesz? Stwierdzam, że reloadDatawraca natychmiast i widzę „END reloadData”, zanim komórki zostaną faktycznie ponownie załadowane (tj. Przed UITableViewDataSourcewywołaniem metod). Moje eksperymenty pokazują dokładne przeciwieństwo tego, co mówisz. Muszę źle zrozumieć, co próbujesz powiedzieć.
Rob
3
Czy odpowiedź Erica nie jest taka sama, jak nie zaimplementowanie (czytaj nie nadpisywanie) reloaddata, wywołanie reloaddata (które będzie zasadniczo [super reloaddata], a następnie po wywołaniu go, rób to, co chcesz po zakończeniu?
Nirav Bhatt
5
Dawno, dawno temu reloadData zakończyło proces ładowania przed jego zakończeniem. Ale Apple zmieniło to w pewnym momencie. Teraz klasa UITableView może buforować wywołanie reloadData z wszystkimi wywołaniami wstawiania i usuwania wierszy. Jeśli spojrzysz na deklarację @interface dla UITableView, znajdziesz element członkowski NSMutableArray _reloadItems tuż pod _insertItems i _deleteItems. (Musiałem przerobić kod, który odziedziczyłem z powodu tej zmiany.)
Walt Sellers,
3
Opublikowania kodu ukończenia w bloku na głównym kolejce po wywołaniu [super reloadData]prace dla mnie dispatch_async(dispatch_get_main_queue(), ^{NSLog(@"reload complete");});. Zasadniczo przeskakuje bloki, które są publikowane przez widok tabeli w reloadData.
Timothy Moose
53

Miałem taki sam scenariusz w mojej aplikacji i pomyślałem, że opublikuję moją odpowiedź dla was, ponieważ inne wymienione tutaj odpowiedzi nie działają dla mnie na iOS7 i później

W końcu to jedyna rzecz, która mi się sprawdziła.

[yourTableview reloadData];

dispatch_async(dispatch_get_main_queue(),^{
        NSIndexPath *path = [NSIndexPath indexPathForRow:yourRow inSection:yourSection];
        //Basically maintain your logic to get the indexpath
        [yourTableview scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionTop animated:YES];
 });

Szybka aktualizacja:

yourTableview.reloadData()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
    let path : NSIndexPath = NSIndexPath(forRow: myRowValue, inSection: mySectionValue)
    //Basically maintain your logic to get the indexpath
    yourTableview.scrollToRowAtIndexPath(path, atScrollPosition: UITableViewScrollPosition.Top, animated: true)

})

Więc jak to działa.

Zasadniczo, gdy wykonujesz przeładowanie, główny wątek staje się zajęty, więc w tym czasie, gdy wykonujemy wysyłanie wątku asynchronicznego, blok będzie czekał, aż główny wątek zostanie zakończony. Tak więc po całkowitym załadowaniu widoku tabeli główny wątek zostanie zakończony, a więc wyśle ​​nasz blok metody

Testowany w iOS7 i iOS8 i działa rewelacyjnie ;)

Aktualizacja dla iOS9: To po prostu działa dobrze, także iOS9. Stworzyłem przykładowy projekt na githubie jako POC. https://github.com/ipraba/TableReloadingNotifier

Załączam tutaj zrzut ekranu z mojego testu.

Testowane środowisko: symulator iPhone6 ​​na iOS9 firmy Xcode7

wprowadź opis obrazu tutaj

ipraba
źródło
8
najlepsza odpowiedź, jaką znalazłem!
Nikolay Shubenkov
@Gon Zrobiłem test w iOS9 i po prostu działa dobrze. Czy możesz polecić github.com/ipraba/TableReloadingNotifier
ipraba
Działa dobrze na moim emulatorze, ale wydaje się, że nie działa na rzeczywistym urządzeniu. Czy ktoś jeszcze ma ten problem?
sosale151
1
To rozwiązanie w końcu działa dla mnie! działa dobrze na iOS 9
Strong
1
Testowałem na prawdziwym urządzeniu, iPhone 6s. To też działa dobrze.
Mocny
25

EDYCJA: Ta odpowiedź w rzeczywistości nie jest rozwiązaniem. Prawdopodobnie wydaje się, że na początku działa, ponieważ ponowne ładowanie może nastąpić dość szybko, ale w rzeczywistości blok uzupełniania niekoniecznie jest wywoływany po całkowitym zakończeniu ponownego ładowania danych - ponieważ reloadData nie blokuje. Prawdopodobnie powinieneś poszukać lepszego rozwiązania.

Aby rozwinąć odpowiedź @Erica MORANDA, wstawmy blok uzupełniania. Kto nie lubi bloku?

@interface DUTableView : UITableView

   - (void) reloadDataWithCompletion:( void (^) (void) )completionBlock;

@end

i...

#import "DUTableView.h"

@implementation DUTableView

- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock {
    [super reloadData];
    if(completionBlock) {
        completionBlock();
    }
}

@end

Stosowanie:

[self.tableView reloadDataWithCompletion:^{
                                            //do your stuff here
                                        }];
bandejapaisa
źródło
2
Nie mam dość bloków. Kto potrzebuje delegata, skoro masz ładny blok!
bandejapaisa
Podoba mi się to, ale co z momentami, gdy system wywołuje reloadData (), np. Kiedy tabela jest wyświetlana po raz pierwszy?
Symmetric
12
To nie jest rozwiązanie Blok ukończenia rozpoczyna wykonywanie przed cellForRowAtIndexPath
zvjerka24
1
To nie zadziała; reloadData nie jest metodą blokującą. Ten kod jest wywoływany natychmiast po wywołaniu reloadData, nawet jeśli komórki nie zostały jeszcze ponownie załadowane. Poza tym spójrz na ten kod, a zobaczysz, że równie dobrze możesz po prostu umieścić swój kod po reloadData .
colinta
1
To działa dla mnie z niewielką zmianą. Zamiast wywoływać blok zakończenia bezpośrednio, nazywają go w bloku zamieszczonych na głównej kolejki: dispatch_async(dispatch_get_main_queue(), ^{completionBlock();});. To w zasadzie przeskakuje bloki umieszczone w widoku tabeli w reloadData.
Timothy Moose
11

reloadData tylko prosząc o dane dla widocznych komórek. Mówi, że aby otrzymać powiadomienie o załadowaniu określonej części tabeli, podłącz tableView: willDisplayCell:metodę.

- (void) reloadDisplayData
{
    isLoading =  YES;
    NSLog(@"Reload display with last index %d", lastIndex);
    [_tableView reloadData];
    if(lastIndex <= 0){
    isLoading = YES;
    //Notify completed
}

- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(indexPath.row >= lastIndex){
    isLoading = NO;
    //Notify completed
}
Czarny koder
źródło
1
Nie jestem pewien, czy to zadziała. Ostatni indeks to koniec danych tabeli (np. 100 rekordów), ale tabela wyświetli tylko to, co jest widoczne na ekranie (np. 8 rekordów).
Travis M.,
10

To jest moje rozwiązanie. W 100% działa i jest używany w wielu projektach. To prosta podklasa UITableView.

@protocol MyTableViewDelegate<NSObject, UITableViewDelegate>
@optional
- (void)tableViewWillReloadData:(UITableView *)tableView;
- (void)tableViewDidReloadData:(UITableView *)tableView;
@end

@interface MyTableView : UITableView {
    struct {
        unsigned int delegateWillReloadData:1;
        unsigned int delegateDidReloadData:1;
        unsigned int reloading:1;
    } _flags;
}
@end

@implementation MyTableView
- (id<MyTableViewDelegate>)delegate {
    return (id<MyTableViewDelegate>)[super delegate];
}

- (void)setDelegate:(id<MyTableViewDelegate>)delegate {
    [super setDelegate:delegate];
    _flags.delegateWillReloadData = [delegate respondsToSelector:@selector(tableViewWillReloadData:)];
    _flags.delegateDidReloadData = [delegate    respondsToSelector:@selector(tableViewDidReloadData:)];
}

- (void)reloadData {
    [super reloadData];
    if (_flags.reloading == NO) {
        _flags.reloading = YES;
        if (_flags.delegateWillReloadData) {
            [(id<MyTableViewDelegate>)self.delegate tableViewWillReloadData:self];
        }
        [self performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f];
    }
}

- (void)finishReload {
    _flags.reloading = NO;
    if (_flags.delegateDidReloadData) {
        [(id<MyTableViewDelegate>)self.delegate tableViewDidReloadData:self];
    }
}

@end

Jest podobny do rozwiązania Josha Browna z jednym wyjątkiem. W metodzie performSelector nie jest potrzebne żadne opóźnienie. Nieważne, ile czasu reloadDatato zajmie. tableViewDidLoadData:zawsze strzela, gdy tableViewkończy pytać dataSource cellForRowAtIndexPath.

Nawet jeśli nie chcesz podklasy UITableView, możesz po prostu zadzwonić, [performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f]a Twój selektor zostanie wywołany zaraz po zakończeniu ładowania tabeli. Należy jednak upewnić się, że selector jest wywoływany tylko raz na wywołanie reloadData:

[self.tableView reloadData];
[self performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f];

Cieszyć się. :)

Mark Kryzhanouski
źródło
1
Korzystanie z funkcji performSelector jest genialne. Proste i działające, dzięki
Julia
To jest absolutnie niesamowite. Walczę z tym od dni. Dziękuję Ci!
David Carrico
@MarkKryzhanouski, czy testowałeś iOS 9?
Victor
@MarkKryzhanouski, dzięki za szybką informację zwrotną! Działa świetnie na iOS 7 i 8.
Victor
Rozwiązania performSelectorlub uruchamianie na głównym wątku z dispatch_asynchnie działają na iOS 9 .
Manuel
8

To odpowiedź na nieco inne pytanie: musiałem wiedzieć, kiedy UITableViewteż skończyłem dzwonić cellForRowAtIndexPath(). I podklasy layoutSubviews()(dzięki @Eric Morand) i dodano wywołania zwrotnego delegata:

SDTableView.h:

@protocol SDTableViewDelegate <NSObject, UITableViewDelegate>
@required
- (void)willReloadData;
- (void)didReloadData;
- (void)willLayoutSubviews;
- (void)didLayoutSubviews;
@end

@interface SDTableView : UITableView

@property(nonatomic,assign) id <SDTableViewDelegate> delegate;

@end;

SDTableView.m:

#import "SDTableView.h"

@implementation SDTableView

@dynamic delegate;

- (void) reloadData {
    [self.delegate willReloadData];

    [super reloadData];

    [self.delegate didReloadData];
}

- (void) layoutSubviews {
    [self.delegate willLayoutSubviews];

    [super layoutSubviews];

    [self.delegate didLayoutSubviews];
}

@end

Stosowanie:

MyTableViewController.h:

#import "SDTableView.h"
@interface MyTableViewController : UITableViewController <SDTableViewDelegate>
@property (nonatomic) BOOL reloadInProgress;

MyTableViewController.m:

#import "MyTableViewController.h"
@implementation MyTableViewController
@synthesize reloadInProgress;

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    if ( ! reloadInProgress) {
        NSLog(@"---- numberOfSectionsInTableView(): reloadInProgress=TRUE");
        reloadInProgress = TRUE;
    }

    return 1;
}

- (void)willReloadData {}
- (void)didReloadData {}
- (void)willLayoutSubviews {}

- (void)didLayoutSubviews {
    if (reloadInProgress) {
        NSLog(@"---- layoutSubviewsEnd(): reloadInProgress=FALSE");
        reloadInProgress = FALSE;
    }
}

UWAGI: Ponieważ jest to podklasa, UITableViewktóra ma już właściwość delegata wskazującą, MyTableViewControllernie ma potrzeby dodawania kolejnej. „@Dynamic delegate” informuje kompilator, aby używał tej właściwości. (Oto link opisujący to: http://farhadnoorzay.com/2012/01/20/objective-c-how-to-add-delegate-methods-in-a-subclass/ )

UITableViewMajątek MyTableViewControllermusi zostać zmieniony, aby korzystać z nowej SDTableViewklasy. Odbywa się to w Inspektorze tożsamości programu Interface Builder. Wybierz UITableViewwnętrze UITableViewControlleri ustaw jego „Custom Class” na SDTableView.

Symetryczny
źródło
Gdzie ustawiasz MyTableViewController jako delegata dla SDTableView? Jak to możliwe, że można ustawić właściwość name „delegate” w SDTableView, skoro jego nadklasa ma już właściwość o tej nazwie (UITableView.delegate)? Jak dołączyć niestandardowy widok SDTableView do właściwości MyTableViewController.tableView, gdy właściwość jest typu „UITablewView”, a obiekt (instancja SDTableView) jest typu „SDTableView”? Walczę z tym samym problemem, więc mam nadzieję, że jest rozwiązanie tych problemów :)
Earl Grey
Czy w kodzie Symmetric (SDTableView) reloadInProgress nie powinno być ustawione na FALSE w didReloadData zamiast didLayoutSubviews? Ponieważ reloadData jest wywoływana po layoutSubviews, a ładowanie nie powinno być uważane za wykonane przed zakończeniem operacji reloadData.
tzuchien.chiu
To nie działa na iOS 8, musiałem uwzględnić odpowiedź iPrabu .
Stunner
6

Znalazłem coś podobnego, aby dostać powiadomienie o zmianie contentSizeod TableView. Myślę, że to również powinno działać tutaj, ponieważ contentSize również zmienia się wraz z ładowaniem danych.

Spróbuj tego:

Na viewDidLoadpiśmie

[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionPrior context:NULL];

i dodaj tę metodę do swojego viewController:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"contentSize"]) {
        DLog(@"change = %@", change.description)

        NSValue *new = [change valueForKey:@"new"];
        NSValue *old = [change valueForKey:@"old"];

        if (new && old) {
            if (![old isEqualToValue:new]) {
                // do your stuff
            }
        }


    }
}

Możesz potrzebować niewielkich modyfikacji w sprawdzaniu zmian. To jednak zadziałało dla mnie.

Twoje zdrowie! :)

Abdullah Umer
źródło
1
Bardzo mądry. U mnie działa idealnie. Dzięki za udostępnienie!
DZenBot
2
Elegancki! Jedyne, co chciałbym dodać, to to, że musisz usunąć siebie i obserwatora (być może w metodzie -dealloc). Lub dodaj siebie jako obserwatora w metodzie -viewWillAppeari usuń się z -viewWillDisapearmetody.
Loozie
2

Oto możliwe rozwiązanie, choć to hack:

[self.tableView reloadData];
[self performSelector:@selector(scrollTableView) withObject:nil afterDelay:0.3];

Gdzie Twoja -scrollTableViewmetoda przewija widok tabeli za pomocą -scrollRectToVisible:animated:. I, oczywiście, możesz skonfigurować opóźnienie w powyższym kodzie od 0.3 do tego, co wydaje się działać dla Ciebie. Tak, to absurdalne, ale działa na moim iPhonie 5 i 4S ...

Josh Brown
źródło
2
Może się to wydawać „zepsute”, ale jest to naprawdę standardowe podejście do przewijania: odłóż je do następnego cyklu uruchamiania. Zmieniłbym opóźnienie na 0, ponieważ działa to równie dobrze i ma mniejsze opóźnienie.
phatmann
Nie działało dla mnie z opóźnieniem ustawionym na 0; 0.3 to najniższy poziom, na jaki mogłem wejść i nadal uzyskać dane.
Josh Brown
@phatmann To zadziałało dla mnie. Mogłem również użyć 0 dla mojego opóźnienia. Dziękuję wam obu.
mcphersonjr
1

Wydaje mi się, że miałem coś podobnego. Dodałem zmienną BOOL jako instancji, która mówi mi, czy przesunięcie zostało przywrócone i sprawdź to -viewWillAppear:. Jeśli nie został przywrócony, przywracam go tą metodą i ustawiam BOOL, aby wskazał, że odzyskałem przesunięcie.

To rodzaj hackowania i prawdopodobnie da się to zrobić lepiej, ale w tej chwili to działa.

Joost
źródło
Tak, problem polega na tym, że viewWillAppear wydaje się być zbyt wczesny (przynajmniej w moim scenariuszu). Próba przywrócenia przesunięcia w viewWillAppear nic nie robi - chyba że najpierw dodam hack wywoływania reloadData. Jak rozumiem, viewWillAppear / viewDidAppear odnoszą się tylko do samego widoku tabeli, który faktycznie się pojawia - nie zgłaszają żadnych roszczeń dotyczących tego, czy komórki tabeli w widoku zostały wyliczone lub zapełnione ... i musi to nastąpić, zanim będzie można odzyskać przesunięcie, ponieważ w przeciwnym razie odzyskasz przesunięcie na pustym widoku (i rozumiem, dlaczego to nie zadziała!).
kennethmac2000
Ale może chodziło Ci o to, że najpierw wymuszasz ładowanie komórek tabeli, wywołując reloadData w ramach viewWillAppear?
kennethmac2000
Nie, nie wymuszam przeładowania. Kiedy miałem problem, próbowałem przywrócić przesunięcie w -viewDidLoad(tam, gdzie to oczywiście powinno się zdarzyć), ale to działało tylko wtedy, gdy ustawiłem przesunięcie jako animowane. Przesuwając ustawienie offsetu, aby -viewWillAppear:działało, ale musiałem zachować flagę, aby ustawić ją tylko raz. Przypuszczam, że widok tabeli ponownie ładuje swoje dane po dodaniu do widoku, więc jest już w -loadView. Czy na pewno masz dostępne dane podczas ładowania widoku? A może jest ładowany w osobnym wątku czy coś?
Joost
OK, może pomożesz mi tutaj zrozumieć. W ten sposób rozumiem sekwencję wywołań, które mają być wykonywane podczas tworzenia UITableView. 1) viewDidLoad jest uruchamiany jako pierwszy. Oznacza to, że UITableView jest ładowany do pamięci. 2) viewWillAppear jest obok ognia. Oznacza to, że UITableView zostanie wyświetlony, ale niekoniecznie, że wszystkie widoczne obiekty UITableViewCell zostaną w pełni utworzone / zakończone renderowanie.
kennethmac2000
2
I wszystko powyższe jest prawdą, pytanie brzmi: jakie nie-hacky mamy opcje, aby dowiedzieć się, kiedy UITableView uzyskał wystarczające informacje ze swojego źródła danych, aby zagwarantować, że żądanie przewijania (tj. ScrollRectToVisible: animated: call ) faktycznie zadziała (a nie tylko nic nie zrobi)?
kennethmac2000
1

Wygląda na to, że chcesz zaktualizować zawartość komórki, ale bez nagłych skoków, które mogą towarzyszyć wstawianiu i usuwaniu komórek.

Jest kilka artykułów na ten temat. To jest jeden.

Sugeruję użycie setContentOffset: animated: zamiast scrollRectToVisible: animated: dla perfekcyjnych ustawień widoku przewijania w pikselach.

Walt Sellers
źródło
1

Możesz wypróbować następującą logikę:

-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyIdentifier"];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"MyIdentifier"];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
    }

    if ( [self chkIfLastCellIndexToCreate:tableView :indexPath]){
        NSLog(@"Created Last Cell. IndexPath = %@", indexPath);
        //[self.activityIndicator hide];
        //Do the task for TableView Loading Finished
    }
    prevIndexPath = indexPath;

    return cell;
}



-(BOOL) chkIfLastCellIndexToCreate:(UITableView*)tableView : (NSIndexPath *)indexPath{

    BOOL bRetVal = NO;
    NSArray *visibleIndices = [tableView indexPathsForVisibleRows];

    if (!visibleIndices || ![visibleIndices count])
        bRetVal = YES;

    NSIndexPath *firstVisibleIP = [visibleIndices objectAtIndex:0];
    NSIndexPath *lastVisibleIP = [visibleIndices objectAtIndex:[visibleIndices count]-1];

    if ((indexPath.row > prevIndexPath.row) && (indexPath.section >= prevIndexPath.section)){
        //Ascending - scrolling up
        if ([indexPath isEqual:lastVisibleIP]) {
            bRetVal = YES;
            //NSLog(@"Last Loading Cell :: %@", indexPath);
        }
    } else if ((indexPath.row < prevIndexPath.row) && (indexPath.section <= prevIndexPath.section)) {
        //Descending - scrolling down
        if ([indexPath isEqual:firstVisibleIP]) {
            bRetVal = YES;
            //NSLog(@"Last Loading Cell :: %@", indexPath);
        }
    }
    return bRetVal;
}

Zanim wywołasz reloadData, ustaw prevIndexPath na nil. Lubić:

prevIndexPath = nil;
[mainTableView reloadData];

Testowałem z NSLogs i ta logika wydaje się być w porządku. W razie potrzeby możesz dostosować / ulepszyć.

Joe M.
źródło
0

wreszcie sprawiłem, że mój kod działa z tym -

[tableView scrollToRowAtIndexPath:scrollToIndex atScrollPosition:UITableViewScrollPositionTop animated:YES];

było kilka rzeczy, którymi należało się zająć -

  1. nazwij to w "- (UITableViewCell *)MyTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  2. po prostu upewnij się, że wiadomość „scrollToRowAtIndexPath” jest wysyłana do odpowiedniego wystąpienia UITableView, którym w tym przypadku jest zdecydowanie MyTableview.
  3. W moim przypadku UIView jest widokiem zawierającym wystąpienie UITableView
  4. Będzie to również wywoływane dla każdego obciążenia ogniwa. Dlatego należy wprowadzić logikę wewnątrz „cellForRowAtIndexPath”, aby uniknąć wielokrotnego wywoływania „scrollToRowAtIndexPath”.
smile.al.d.way
źródło
i po prostu upewnij się, że ten utwór nie zostanie wywołany więcej niż raz. jeśli nie, blokuje przewijanie.
smile.al.d.way
To może działać, ale zdecydowanie nie jest eleganckie i nie jest rozwiązaniem, którego szukam. Niestety, wydaje się, że nie ma lepszego sposobu na ustalenie, czy -reloadData faktycznie zakończyło pobieranie danych ...
Josh Brown
0

Możesz zmienić rozmiar widoku tabeli lub ustawić rozmiar zawartości w tej metodzie po załadowaniu wszystkich danych:

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    tableView.frame =CGRectMake(tableView.frame.origin.x, tableView.frame.origin.y, tableView.frame.size.width, tableView.contentSize.height);
}
Banker Mittal
źródło
0

Po prostu uruchamiam powtarzanie zaplanowanego timera i unieważniam go tylko wtedy, gdy contentSize tabeli jest większy, gdy wysokość tableHeaderView (oznacza, że ​​w tabeli jest zawartość wierszy). Kod w C # (monotouch), ale mam nadzieję, że pomysł jest jasny:

    public override void ReloadTableData()
    {
        base.ReloadTableData();

        // don't do anything if there is no data
        if (ItemsSource != null && ItemsSource.Length > 0)
        {
            _timer = NSTimer.CreateRepeatingScheduledTimer(TimeSpan.MinValue, 
                new NSAction(() => 
                {
                    // make sure that table has header view and content size is big enought
                    if (TableView.TableHeaderView != null &&
                        TableView.ContentSize.Height > 
                            TableView.TableHeaderView.Frame.Height)
                    {
                        TableView.SetContentOffset(
                            new PointF(0, TableView.TableHeaderView.Frame.Height), false);
                        _timer.Invalidate();
                        _timer = null;
                    }
                }));
        }
    }
Eugene Tiutiunnyk
źródło
0

Nie jest UITableView layoutSubviewswywoływana tuż przed wyświetleniem zawartości w widoku tabeli? Zauważyłem, że jest on wywoływany, gdy widok tabeli zakończy ładowanie danych, może powinieneś zbadać w tym kierunku.

Eric MORAND
źródło
0

Od wersji iOS 6 UITableviewmetoda delegata o nazwie:

-(void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section

zostanie wykonany po pomyślnym załadowaniu tabeli. W tej metodzie możesz dostosować ustawienia zgodnie z wymaganiami.

Bharath Vankireddy
źródło
0

Najlepsze rozwiązanie jakie znalazłem w Swift

extension UITableView {
    func reloadData(completion: ()->()) {
        self.reloadData()
        dispatch_async(dispatch_get_main_queue()) {
            completion()
        }
    }
}
YannSteph
źródło
-1

Dlaczego po prostu nie przedłużyć?

@interface UITableView(reloadComplete)
- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock;
@end

@implementation UITableView(reloadComplete)
- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock {
    [self reloadData];
    if(completionBlock) {
        completionBlock();
    }
}
@end

przewiń do końca:

[self.table reloadDataWithCompletion:^{
    NSInteger numberOfRows = [self.table numberOfRowsInSection:0];
    if (numberOfRows > 0)
    {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:numberOfRows-1 inSection:0];
        [self.table scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
    }
}];

Nie testowano z dużą ilością danych

Andrii Tishchenko
źródło