Ricevi una notifica quando UITableView ha finito di chiedere i dati?


108

C'è un modo per scoprire quando un UITableViewha finito di chiedere i dati dalla sua origine dati?

Nessuno dei metodi viewDidLoad/ viewWillAppear/ viewDidAppeardel controller di visualizzazione associato ( UITableViewController) è utile qui, poiché si attivano tutti troppo presto. Nessuno di questi (del tutto comprensibilmente) garantisce che le query all'origine dati siano terminate per il momento (ad esempio, fino a quando la vista non viene fatta scorrere).

Una soluzione che ho trovato è di chiamare reloadDataa viewDidAppear, dal momento che, quando reloadDatatorna, la visualizzazione della tabella è garantito di aver finito l'interrogazione l'origine dei dati tanto quanto deve per il momento.

Tuttavia, questo sembra piuttosto sgradevole, poiché presumo stia facendo sì che all'origine dati vengano richieste le stesse informazioni due volte (una volta automaticamente e una volta a causa della reloadDatachiamata) quando viene caricata per la prima volta.

Il motivo per cui voglio farlo è che voglio preservare la posizione di scorrimento del UITableView- ma fino al livello dei pixel, non solo alla riga più vicina.

Quando si ripristina la posizione di scorrimento (utilizzando scrollRectToVisible:animated:), è necessario che la vista tabella contenga già dati sufficienti, altrimenti la scrollRectToVisible:animated:chiamata al metodo non fa nulla (che è ciò che accade se si effettua la chiamata da sola in uno qualsiasi di viewDidLoad, viewWillAppearo viewDidAppear).


Sembra che tu stia cercando qualcosa di simile a questo stackoverflow.com/a/11672379/2082172 .
Timur Kuchkarov

Nella mia esperienza, UITableView può memorizzare nella cache le chiamate a reloadData, proprio come memorizza nella cache le chiamate per inserire / eliminare righe e altre cose. UITableView chiamerà il delegato dell'origine dati quando è pronto, a seconda dell'utilizzo della CPU e di altre cose.
Walt Sellers

Risposte:


61

Questa risposta sembra non funzionare più, a causa di alcune modifiche apportate all'implementazione di UITableView da quando è stata scritta la risposta. Vedi questo commento: Ricevi una notifica quando UITableView ha finito di chiedere dati?

Ho giocato con questo problema per un paio di giorni e credo che sottoclassi UITableView's reloadDataè l'approccio migliore:

- (void)reloadData {

    NSLog(@"BEGIN reloadData");

    [super reloadData];

    NSLog(@"END reloadData");

}

reloadDatanon finisce prima che la tabella abbia terminato di ricaricare i suoi dati. Quindi, quando il secondoNSLog viene attivato , la visualizzazione tabella ha effettivamente finito di richiedere i dati.

Ho sottoclasse UITableViewper inviare metodi al delegato prima e dopo reloadData. Esso funziona magicamente.


2
Devo dire che questa sembra essere la soluzione migliore. L'ho appena implementato e, come dici tu, funziona a meraviglia. Grazie!
teoria

2
@EricMORAND Dici che "reloadData non finisce prima che la tabella abbia terminato di ricaricare i suoi dati." Puoi chiarire cosa intendi con questo? Trovo che reloadDataritorni immediatamente e vedo "END reloadData" prima che le celle vengano effettivamente ricaricate (cioè prima che i UITableViewDataSourcemetodi vengano chiamati). La mia sperimentazione dimostra l'esatto contrario di quello che dici. Devo fraintendere quello che stai cercando di dire.
Rob

3
La risposta di Eric non è come non implementare (leggere senza sovrascrivere) reloaddata, chiamare reloaddata (che sarà essenzialmente [super reloaddata], e poi dopo averlo chiamato, fare le cose che vuoi al suo completamento?
Nirav Bhatt

5
C'era una volta, reloadData ha completato il processo di caricamento prima che finisse. Ma Apple lo ha cambiato ad un certo punto. Ora la classe UITableView può memorizzare nella cache la chiamata reloadData con tutte le chiamate di inserimento ed eliminazione delle righe. Se guardi la dichiarazione @interface per UITableView, troverai _reloadItems membro NSMutableArray proprio sotto _insertItems e _deleteItems. (Ho dovuto rielaborare il codice che ho ereditato a causa di questa modifica.)
Walt Sellers

3
La pubblicazione del codice di completamento in un blocco sulla coda principale dopo aver chiamato [super reloadData]funziona per me: dispatch_async(dispatch_get_main_queue(), ^{NSLog(@"reload complete");});. Fondamentalmente salta i blocchi che vengono pubblicati dalla visualizzazione tabella in reloadData.
Timothy Moose

53

Avevo lo stesso scenario nella mia app e pensavo di pubblicare la mia risposta a voi ragazzi poiché altre risposte menzionate qui non funzionano per me per iOS7 e versioni successive

Finalmente questa è l'unica cosa che ha funzionato per me.

[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];
 });

Aggiornamento rapido:

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)

})

Allora come funziona.

Fondamentalmente quando esegui un ricaricamento il thread principale diventa occupato, quindi in quel momento in cui eseguiamo un thread asincrono di invio, il blocco aspetterà fino al termine del thread principale. Quindi, una volta che la tableview è stata caricata completamente, il thread principale verrà terminato e quindi invierà il nostro blocco del metodo

Testato in iOS7 e iOS8 e funziona alla grande;)

Aggiornamento per iOS9: funziona bene anche per iOS9. Ho creato un progetto di esempio in GitHub come POC. https://github.com/ipraba/TableReloadingNotifier

Allego qui lo screenshot del mio test.

Ambiente testato: simulatore iOS9 iPhone6 ​​da Xcode7

inserisci qui la descrizione dell'immagine


8
la migliore risposta che ho trovato!
Nikolay Shubenkov

@Gon ho fatto un test su iOS9 e funziona perfettamente. Puoi fare riferimento a github.com/ipraba/TableReloadingNotifier
ipraba

Funziona bene sul mio emulatore ma non sembra funzionare sul dispositivo reale. Qualcun altro ha questo problema?
sosale151

1
Questa soluzione finalmente funziona per me! funziona bene su iOS 9
Strong

1
Ho provato su Real Device, un iPhone 6s. Funziona anche bene.
Forte

25

EDIT: questa risposta non è in realtà una soluzione. Probabilmente all'inizio sembra funzionare perché il ricaricamento può avvenire abbastanza velocemente, ma in realtà il blocco di completamento non viene necessariamente chiamato dopo che i dati hanno terminato completamente il ricaricamento, perché reloadData non si blocca. Probabilmente dovresti cercare una soluzione migliore.

Per espandere la risposta di @Eric MORAND, inseriamo un blocco di completamento. Chi non ama un blocco?

@interface DUTableView : UITableView

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

@end

e...

#import "DUTableView.h"

@implementation DUTableView

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

@end

Uso:

[self.tableView reloadDataWithCompletion:^{
                                            //do your stuff here
                                        }];

2
Non ne ho mai abbastanza di blocchi. Chi ha bisogno di un delegato quando hai un bel blocco!
bandejapaisa

Mi piace, ma per quanto riguarda le volte in cui il sistema chiama reloadData (), ad esempio quando la tabella viene visualizzata per la prima volta?
Simmetrico

12
Questa non è una soluzione Il blocco di completamento inizia l'esecuzione prima di cellForRowAtIndexPath
zvjerka24

1
Questo non funzionerà; reloadData non è un metodo di blocco. Questo codice viene chiamato immediatamente dopo aver chiamato reloadData, anche se le celle non sono ancora state ricaricate. Inoltre, guarda questo codice e vedrai che potresti anche inserire il tuo codice dopo reloadData .
colinta

1
Questo funziona per me con una piccola modifica. Invece di chiamare direttamente il blocco di completamento, lo chiamano in un blocco pubblicato sulla coda principale: dispatch_async(dispatch_get_main_queue(), ^{completionBlock();});. Questo fondamentalmente salta i blocchi pubblicati dalla visualizzazione tabella in reloadData.
Timothy Moose

11

reloadData richiede solo i dati per le celle visibili. Dice, per essere avvisato quando viene caricata una parte specifica della tua tabella, aggancia il tableView: willDisplayCell:metodo.

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

1
Non sono sicuro che funzioni. L'ultimo indice è la fine dei dati della tabella (es. 100 record) ma la tabella mostrerà solo ciò che è visibile sullo schermo (es. 8 record).
Travis M.

10

Questa è la mia soluzione. Funziona al 100% e viene utilizzato in molti progetti. È una semplice sottoclasse 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

È simile alla soluzione di Josh Brown con un'eccezione. Non è necessario alcun ritardo nel metodo performSelector. Non importa quanto tempo ci reloadDatavuole. tableViewDidLoadData:spara sempre quando tableViewfinisce di chiedere dataSource cellForRowAtIndexPath.

Anche se non vuoi UITableViewcreare una sottoclasse , puoi semplicemente chiamare [performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f]e il tuo selettore verrà chiamato subito dopo che la tabella ha finito di ricaricarsi. Ma dovresti assicurarti che il selettore venga chiamato solo una volta per chiamata a reloadData:

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

Godere. :)


1
L'utilizzo della chiamata performSelector è brillante. Semplice e funzionante, grazie
Julia

Questo è assolutamente fantastico. Ci sto discutendo da giorni. Grazie!
David Carrico

@MarkKryzhanouski, hai provato su iOS 9?
Victor

@MarkKryzhanouski, grazie per il tempestivo feedback! Funziona alla grande su iOS 7 e 8.
Victor

Le soluzioni performSelectoro l'esecuzione sul thread principale con dispatch_asynchnon funzionano su iOS 9 .
Manuel

8

Questa è una risposta a una domanda leggermente diversa: avevo bisogno di sapere anche quando UITableViewavevo finito di chiamare cellForRowAtIndexPath(). Ho sottoclasse layoutSubviews()(grazie @Eric MORAND) e ho aggiunto una richiamata 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

Uso:

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

NOTE: Poiché questa è una sottoclasse di UITableViewcui ha già una proprietà delegato che punta a MyTableViewControllernon è necessario aggiungerne un'altra. Il "@dynamic delegate" dice al compilatore di utilizzare questa proprietà. (Ecco un collegamento che descrive questo: http://farhadnoorzay.com/2012/01/20/objective-c-how-to-add-delegate-methods-in-a-subclass/ )

La UITableViewproprietà in MyTableViewControllerdeve essere modificata per utilizzare la nuova SDTableViewclasse. Questa operazione viene eseguita in Interface Builder Identity Inspector. Seleziona l' UITableViewinterno di UITableViewControllere imposta la sua "Classe personalizzata" su SDTableView.


Dove si imposta MyTableViewController come delegato per SDTableView? Come mai è possibile impostare una proprietà di nome "delegate" su SDTableView, quando la sua superclasse ha già una proprietà di quel nome (UITableView.delegate)? Come si collega il proprio SDTableView personalizzato alla proprietà MyTableViewController.tableView quando la proprietà è di tipo "UITablewView" e l'oggetto (un'istanza di SDTableView) è di tipo "SDTableView"? Sto combattendo lo stesso problema, quindi spero che ci sia una soluzione a questi problemi :)
Earl Grey

Nel codice di Symmetric (SDTableView), reloadInProgress non dovrebbe essere impostato su FALSE in didReloadData invece di didLayoutSubviews? Perché reloadData viene chiamato dopo layoutSubviews e il caricamento non deve essere considerato eseguito prima del termine di reloadData.
tzuchien.chiu

Questo non funziona per iOS 8, ha dovuto incorporare la risposta di iPrabu .
Storditore

6

Avevo trovato qualcosa di simile per ricevere una notifica per il cambio contentSizediTableView . Penso che dovrebbe funzionare anche qui poiché contentSize cambia anche con il caricamento dei dati.

Prova questo:

Per viewDidLoadiscritto

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

e aggiungi questo metodo al tuo 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
            }
        }


    }
}

Potrebbero essere necessarie lievi modifiche nel controllo delle modifiche. Per me questo aveva funzionato.

Saluti! :)


1
Molto intelligente. Funziona perfettamente per me. Grazie per la condivisione!
DZenBot

2
Elegante! L'unica cosa che aggiungerei è che devi rimuovere te stesso e un osservatore (forse nel metodo -dealloc). Oppure aggiungi te stesso come osservatore nel metodo -viewWillAppeare rimuoviti nel -viewWillDisapearmetodo.
Loozie

2

Ecco una possibile soluzione, sebbene sia un hack:

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

Dove il tuo -scrollTableViewmetodo scorre la vista tabella con -scrollRectToVisible:animated:. E, naturalmente, puoi configurare il ritardo nel codice sopra da 0.3 a qualunque cosa sembri funzionare per te. Sì, è ridicolmente hacky, ma funziona per me sul mio iPhone 5 e 4S ...


2
Può sembrare "hacky", ma questo è davvero un approccio standard per lo scorrimento: rimandalo al ciclo di esecuzione successivo. Modificherei il ritardo a 0, poiché funziona altrettanto bene e ha meno latenza.
phatmann

Non ha funzionato per me con il ritardo impostato su 0; 0,3 era il minimo che potevo ottenere e ancora ottenere i dati.
Josh Brown

@phatmann Questo ha funzionato per me. Sono stato anche in grado di utilizzare 0 per il mio ritardo. Grazie a tutti e due.
mcphersonjr

1

Ho avuto qualcosa di simile credo. Ho aggiunto un BOOL come variabile di istanza che mi dice se l'offset è stato ripristinato e controllalo-viewWillAppear: . Quando non è stato ripristinato, lo ripristino con quel metodo e imposto BOOL per indicare che ho recuperato l'offset.

È una specie di hack e probabilmente può essere fatto meglio, ma per me al momento funziona.


Sì, il problema è che viewWillAppear sembra essere troppo presto (almeno nel mio scenario). Il tentativo di ripristinare l'offset in viewWillAppear non fa nulla, a meno che non aggiungo prima l'hack per chiamare reloadData. A quanto ho capito, viewWillAppear / viewDidAppear si riferiscono solo alla vista tabella stessa che appare effettivamente - non fanno alcuna affermazione sul fatto che le celle della tabella nella vista siano state enumerate o popolate ... e questo deve accadere prima che tu possa recuperare un offset, altrimenti recupereresti un offset su una vista vuota (e capisco perché non funzionerà!).
kennethmac2000

Ma forse volevi dire che stai anche forzando prima il caricamento delle celle della tabella chiamando reloadData all'interno di viewWillAppear?
kennethmac2000

No, non sto forzando una ricarica. Quando ho avuto il problema, ho provato a ripristinare l'offset in -viewDidLoad(dove dovrebbe accadere ovviamente) ma ha funzionato solo quando ho impostato l'offset animato. Spostando l'impostazione dell'offset su -viewWillAppear:esso ha funzionato, ma ho dovuto mantenere un flag per impostarlo solo una volta. Suppongo che la vista tabella ricarichi i suoi dati una volta aggiunti a una vista, quindi è già presente -loadView. Sei sicuro di avere i tuoi dati disponibili al caricamento della visualizzazione? O viene caricato in un thread separato o qualcosa del genere?
Joost

OK, forse puoi aiutare la mia comprensione qui. Questo è il modo in cui intendo la sequenza di chiamate quando viene creato un UITableView. 1) viewDidLoad viene attivato per primo. Ciò indica che UITableView è caricato in memoria. 2) viewWillAppear è vicino al fuoco. Ciò indica che verrà visualizzato UITableView, ma non necessariamente che tutti gli oggetti UITableViewCell visibili saranno completamente istanziati / rendering terminati.
kennethmac2000

2
E tutto quanto sopra è vero, la domanda quindi è: quali opzioni non hacker abbiamo per scoprire quando UITableView ha ottenuto informazioni sufficienti dalla sua origine dati per garantire che una richiesta di scorrimento (cioè, scrollRectToVisible: animated: call ) funzionerà davvero (e non semplicemente non farà nulla)?
kennethmac2000

1

Sembra che tu voglia aggiornare il contenuto della cella, ma senza i salti improvvisi che possono accompagnare gli inserimenti e le eliminazioni delle celle.

Ci sono diversi articoli su come farlo. Questo è uno.

Suggerisco di usare setContentOffset: animated: invece di scrollRectToVisible: animated: per impostazioni pixel perfette di una visualizzazione a scorrimento.


1

Puoi provare la seguente logica:

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

E prima di chiamare reloadData, imposta prevIndexPath su nil. Piace:

prevIndexPath = nil;
[mainTableView reloadData];

Ho provato con NSLogs e questa logica sembra ok. Puoi personalizzare / migliorare secondo necessità.


0

finalmente ho fatto funzionare il mio codice con questo -

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

c'erano poche cose di cui prendersi cura -

  1. chiamalo dentro "- (UITableViewCell *)MyTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath "
  2. assicurati solo che il messaggio "scrollToRowAtIndexPath" venga inviato all'istanza pertinente di UITableView, che in questo caso è sicuramente MyTableview.
  3. Nel mio caso UIView è la vista che contiene l'istanza di UITableView
  4. Inoltre, questo verrà richiesto per ogni carico di cella. Pertanto, inserire una logica all'interno di "cellForRowAtIndexPath" per evitare di chiamare "scrollToRowAtIndexPath" più di una volta.

e assicurati solo che questo pezzo non venga chiamato più di una volta. in caso contrario blocca lo scorrimento.
smile.al.d.way

Può funzionare, ma sicuramente non è elegante e non è la soluzione che sto cercando. Sfortunatamente, non sembra esserci un modo migliore per determinare se -reloadData ha effettivamente finito di ottenere i dati ...
Josh Brown

0

Puoi ridimensionare la visualizzazione della tabella o impostarne la dimensione del contenuto in questo metodo quando tutti i dati sono stati caricati:

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

0

Eseguo semplicemente il timer programmato ripetuto e lo invalido solo quando il contentSize della tabella è più grande quando l'altezza di tableHeaderView (significa che c'è un contenuto di righe nella tabella). Il codice in C # (monotouch), ma spero che l'idea sia chiara:

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

0

Non viene UITableView layoutSubviewschiamato appena prima che la vista tabella ne visualizzi il contenuto? Ho notato che viene chiamato una volta che la vista tabella ha terminato di caricare i suoi dati, forse dovresti indagare in quella direzione.


0

Da iOS 6 in poi, il UITableviewmetodo delegato ha chiamato:

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

verrà eseguito una volta che la tabella verrà ricaricata correttamente. È possibile eseguire la personalizzazione come richiesto in questo metodo.


0

La migliore soluzione che ho trovato in Swift

extension UITableView {
    func reloadData(completion: ()->()) {
        self.reloadData()
        dispatch_async(dispatch_get_main_queue()) {
            completion()
        }
    }
}

-1

Perché non estendersi?

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

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

scorri fino alla fine:

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

Non testato con molti dati

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.