Ser notificado quando UITableView terminar de solicitar dados?

108

Existe alguma maneira de descobrir quando um UITableViewterminou de solicitar dados de sua fonte de dados?

Nenhum dos métodos viewDidLoad/ viewWillAppear/ viewDidAppeardo controlador de visualização associado ( UITableViewController) é útil aqui, pois todos eles disparam muito cedo. Nenhum deles (de forma totalmente compreensível) garante que as consultas à fonte de dados tenham terminado por enquanto (por exemplo, até que a exibição seja rolada).

Uma solução que eu encontrei é para chamar reloadDataem viewDidAppear, uma vez que, quando reloadDataretorna, a exibição de tabela é garantida para terminar de consultar a fonte de dados, tanto quanto ele precisa para o momento.

No entanto, isso parece bastante desagradável, pois suponho que esteja fazendo com que a fonte de dados seja solicitada as mesmas informações duas vezes (uma vez automaticamente e uma vez por causa da reloadDatachamada) quando é carregada pela primeira vez.

O motivo pelo qual quero fazer isso é que desejo preservar a posição de rolagem do UITableView- mas até o nível de pixel, não apenas para a linha mais próxima.

Ao restaurar a posição de rolagem (usando scrollRectToVisible:animated:), preciso que o table view já tenha dados suficientes, ou então a scrollRectToVisible:animated:chamada do método não fará nada (que é o que acontece se você colocar a chamada sozinha em qualquer um de viewDidLoad, viewWillAppearou viewDidAppear).

kennethmac2000
fonte
Parece que você está procurando algo semelhante a este stackoverflow.com/a/11672379/2082172 .
Timur Kuchkarov
Na minha experiência, UITableView pode armazenar chamadas em cache para reloadData, assim como ele armazena chamadas para inserir / excluir linhas e outras coisas. UITableView irá chamar o delegado da fonte de dados quando estiver pronto, dependendo do uso da CPU e outras coisas.
Walt Sellers de

Respostas:

61

Esta resposta parece não estar mais funcionando, devido a algumas alterações feitas na implementação de UITableView desde que a resposta foi escrita. Veja este comentário: Ser notificado quando UITableView terminar de solicitar dados?

Eu tenho jogado com esse problema por um par de dias e acho que subclasses UITableView's reloadDataé a melhor abordagem:

- (void)reloadData {

    NSLog(@"BEGIN reloadData");

    [super reloadData];

    NSLog(@"END reloadData");

}

reloadDatanão termina antes que a tabela termine de recarregar seus dados. Portanto, quando o segundo NSLogé disparado, a visualização da tabela acabou de solicitar os dados.

Criei uma subclasse UITableViewpara enviar métodos ao delegado antes e depois reloadData. Ele funciona como um encanto.

Eric MORAND
fonte
2
Tenho que dizer que essa parece ser a melhor solução. Acabei de implementá-lo e, como você disse, funciona perfeitamente. Obrigado!
teoria de
2
@EricMORAND Você diz que "reloadData não termina antes que a tabela termine de recarregar seus dados." Você pode esclarecer o que você quer dizer com isso? Acho que reloadDataretorna imediatamente e vejo "END reloadData" antes que as células sejam realmente recarregadas (ou seja, antes que os UITableViewDataSourcemétodos sejam chamados). Minha experimentação demonstra exatamente o oposto do que você diz. Devo entender mal o que você está tentando dizer.
Rob
3
A resposta de Eric não é a mesma que não implementar (leia, não substituir) reloaddata, chamar reloaddata (que será essencialmente [super reloaddata] e, depois de chamá-lo, fazer as coisas que você deseja na sua conclusão?
Nirav Bhatt
5
Era uma vez, reloadData completou o processo de carregamento antes de terminar. Mas a Apple mudou isso em algum momento. Agora a classe UITableView pode armazenar em cache a chamada reloadData com todas as chamadas de inserção e exclusão de linha. Se você olhar a declaração @interface para UITableView, encontrará o membro NSMutableArray _reloadItems logo abaixo de _insertItems e _deleteItems. (Tive de retrabalhar o código que herdei por causa dessa mudança.)
Walt Sellers
3
Postar o código de conclusão em um bloco na fila principal depois de chamar [super reloadData]funciona para mim: dispatch_async(dispatch_get_main_queue(), ^{NSLog(@"reload complete");});. Basicamente, ele pula os blocos que são postados pela visualização da tabela reloadData.
Timothy Moose
53

Eu tive o mesmo cenário em meu aplicativo e pensei em postar minha resposta para vocês, já que outras respostas mencionadas aqui não funcionam para mim para iOS7 e posterior

Finalmente, esta é a única coisa que funcionou para mim.

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

Atualização rápida:

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)

})

Então, como isso funciona.

Basicamente, quando você faz uma recarga, a thread principal fica ocupada, então, no momento em que fazemos um thread de despacho assíncrono, o bloco irá esperar até que a thread principal seja concluída. Assim que o tableview for carregado completamente, a thread principal será concluída e enviará nosso bloco de métodos

Testado em iOS7 e iOS8 e funciona muito bem;)

Atualização para iOS9: Funciona bem no iOS9 também. Eu criei um projeto de amostra no github como um POC. https://github.com/ipraba/TableReloadingNotifier

Estou anexando a captura de tela do meu teste aqui.

Ambiente testado: simulador iOS9 iPhone6 ​​da Xcode7

insira a descrição da imagem aqui

iPrabu
fonte
8
melhor resposta que encontrei!
Nikolay Shubenkov
@Gon eu fiz um teste no iOS9 e funcionou bem. Você pode consultar github.com/ipraba/TableReloadingNotifier
iPrabu
Funciona bem no meu emulador, mas não parece funcionar no dispositivo real. Alguém mais tendo esse problema?
sosale151
1
Esta solução finalmente funciona para mim! funciona bem no iOS 9
Forte
1
Eu testei no Real Device, um iPhone 6s. Também funciona bem.
Forte
25

EDIT: Esta resposta não é realmente uma solução. Provavelmente parece funcionar a princípio porque o recarregamento pode acontecer muito rápido, mas, na verdade, o bloco de conclusão não é necessariamente chamado depois que o recarregamento completo dos dados - porque reloadData não bloqueia. Você provavelmente deve procurar uma solução melhor.

Para expandir a resposta de @Eric MORAND, vamos colocar um bloco de conclusão. Quem não ama um bloco?

@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
                                        }];
bandejapaisa
fonte
2
Não me canso de blocos. Quem precisa de um delegado quando você tem um bom bloco!
bandejapaisa
Eu gosto disso, mas e quando o sistema chama reloadData (), por exemplo, quando a tabela é exibida pela primeira vez?
Simétrico
12
Isso não é solução O bloco de conclusão inicia a execução antes de cellForRowAtIndexPath
zvjerka24
1
Isso não funcionará; reloadData não é um método de bloqueio. Este código é chamado imediatamente após a chamada de reloadData, mesmo se as células ainda não foram recarregadas. Além disso, olhe para este código e você verá que você pode simplesmente colocar seu código após reloadData .
colinta
1
Isso funciona para mim com uma pequena alteração. Em vez de chamar o bloco conclusão diretamente, chamá-lo em um bloco postado na fila principal: dispatch_async(dispatch_get_main_queue(), ^{completionBlock();});. Isso basicamente pula os blocos postados pela visualização da tabela reloadData.
Timothy Moose
11

reloadData apenas pedindo dados para as células visíveis. Diz que, para ser notificado quando uma parte específica de sua tabela for carregada, conecte o tableView: willDisplayCell:método.

- (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
}
Black Coder
fonte
1
Não tenho certeza se isso funciona. O último índice é o fim dos dados da tabela (por exemplo, 100 registros), mas a tabela exibirá apenas o que está visível na tela (por exemplo, 8 registros).
Travis M.
10

Essa é a minha solução. 100% funciona e é usado em muitos projetos. É uma subclasse UITableView simples.

@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

É semelhante à solução de Josh Brown, com uma exceção. Nenhum atraso é necessário no método performSelector. Não importa o quanto reloadDatademore. tableViewDidLoadData:sempre dispara quando tableViewacaba de perguntar dataSource cellForRowAtIndexPath.

Mesmo se você não quiser uma subclasse, UITableViewvocê pode simplesmente chamar [performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f]e seu seletor será chamado assim que a tabela terminar de recarregar. Mas você deve garantir que o seletor seja chamado apenas uma vez por chamada para reloadData:

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

Aproveitar. :)

Mark Kryzhanouski
fonte
1
Usar a chamada performSelector é brilhante. Simples e funcionando, obrigado
Julia
Isso é absolutamente incrível. Estou discutindo com isso há dias. Obrigado!
David Carrico
@MarkKryzhanouski, você testou no iOS 9?
Victor
@MarkKryzhanouski, obrigado pelo feedback imediato! Funciona muito bem no iOS 7 e 8.
Victor,
As soluções performSelectorou a execução no thread principal dispatch_asynchnão funcionam no iOS 9 .
Manuel de
8

Esta é uma resposta a uma pergunta um pouco diferente: eu precisava saber quando UITableViewtambém havia terminado de ligar cellForRowAtIndexPath(). Eu layoutSubviews()criei uma subclasse (obrigado @Eric MORAND) e adicionei um retorno de chamada delegado:

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

NOTAS: Como esta é uma subclasse da UITableViewqual já possui uma propriedade delegate apontando para, MyTableViewControllernão há necessidade de adicionar outra. O "delegado @dynamic" diz ao compilador para usar esta propriedade. (Aqui está um link que descreve isso: http://farhadnoorzay.com/2012/01/20/objective-c-how-to-add-delegate-methods-in-a-subclass/ )

A UITableViewpropriedade em MyTableViewControllerdeve ser alterada para usar a nova SDTableViewclasse. Isso é feito no Interface Builder Identity Inspector. Selecione o UITableViewinterior de UITableViewControllere defina sua "Classe personalizada" como SDTableView.

Simétrico
fonte
Onde você define seu MyTableViewController para ser o delegado para o SDTableView? Como é possível definir uma propriedade de nome "delegado" em SDTableView, quando sua superclasse já possui uma propriedade com esse nome (UITableView.delegate)? Como você anexa seu SDTableView personalizado à propriedade MyTableViewController.tableView quando a propriedade é do tipo "UITablewView" e o objeto (uma instância de SDTableView) é do tipo "SDTableView"? Estou lutando contra o mesmo problema, então tenho esperança de que haja uma solução para esses problemas :)
Earl Gray
No código de Symmetric (SDTableView), reloadInProgress não deve ser definido como FALSE em didReloadData em vez de didLayoutSubviews? Porque reloadData é chamado após layoutSubviews e o carregamento não deve ser considerado feito antes de reloadData terminar.
tzuchien.chiu
Isso não funciona para iOS 8, teve que incorporar a resposta do iPrabu .
Stunner de
6

Eu tinha encontrado algo semelhante para receber notificação de mudança contentSizede TableView. Acho que deve funcionar aqui também, já que contentSize também muda com o carregamento de dados.

Experimente isto:

Por viewDidLoadescrito,

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

e adicione este método ao seu 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
            }
        }


    }
}

Você pode precisar de pequenas modificações na verificação de mudança. Isso funcionou para mim, no entanto.

Felicidades! :)

Abdullah Umer
fonte
1
Muito esperto. Funciona perfeitamente para mim. Obrigado por compartilhar!
DZenBot
2
Elegante! A única coisa que gostaria de acrescentar é que você precisa remover você e um observador (talvez no método -dealloc). Ou adicione-se como observador no -viewWillAppeare remova-se do -viewWillDisapearmétodo.
Loozie
2

Aqui está uma solução possível, embora seja um hack:

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

Onde seu -scrollTableViewmétodo rola a visualização da tabela -scrollRectToVisible:animated:. E, claro, você pode configurar o atraso no código acima de 0.3 para o que parece funcionar para você. Sim, é ridiculamente hacky, mas funciona para mim no meu iPhone 5 e 4S ...

Josh Brown
fonte
2
Pode parecer "hacky", mas esta é realmente uma abordagem padrão para rolagem: adie para o próximo ciclo de execução. Eu mudaria o atraso para 0, já que funciona tão bem e tem menos latência.
phatmann
Não funcionou para mim com o atraso definido como 0; 0,3 foi o valor mais baixo que consegui obter e ainda obter os dados.
Josh Brown de
@phatmann Isso funcionou para mim. Também consegui usar 0 para meu atraso. Obrigado a vocês dois.
mcphersonjr
1

Eu tinha algo semelhante, eu acredito. Eu adicionei um BOOL como variável de instância que me diz se o deslocamento foi restaurado e verifique isso -viewWillAppear:. Quando não foi restaurado, eu o restauro nesse método e defino o BOOL para indicar que recuperei o deslocamento.

É uma espécie de hack e provavelmente pode ser feito melhor, mas funciona para mim no momento.

Joost
fonte
Sim, o problema é que viewWillAppear parece ser muito cedo (pelo menos no meu cenário). Tentar restaurar o deslocamento em viewWillAppear não faz nada - a menos que eu adicione o hack de chamar reloadData primeiro. Pelo que entendi, viewWillAppear / viewDidAppear se refere apenas à exibição da tabela propriamente dita - eles não fazem nenhuma afirmação sobre se as células da tabela na exibição foram enumeradas ou preenchidas ... e isso precisa acontecer antes que você possa recuperar um deslocamento, caso contrário, você estaria recuperando um deslocamento em uma exibição vazia (e eu entendo por que isso não funciona!).
kennethmac2000
Mas talvez você quisesse dizer que também está forçando o carregamento das células da tabela primeiro, chamando reloadData em viewWillAppear?
kennethmac2000
Não, não estou forçando uma recarga. Quando tive o problema, tentei restaurar o deslocamento em -viewDidLoad(onde deveria acontecer, é claro), mas isso só funcionou quando configurei o deslocamento animado. Ao mover a configuração do deslocamento para -viewWillAppear:ele funcionou, mas eu tive que manter um sinalizador para configurá-lo apenas uma vez. Suponho que a exibição de tabela recarregue seus dados uma vez adicionados a uma exibição, de modo que já está em -loadView. Tem certeza de que tem seus dados disponíveis no carregamento de visualização? Ou está sendo carregado em uma thread separada ou algo assim?
Joost
OK, talvez você possa ajudar minha compreensão aqui. É assim que eu entendo a sequência de chamadas quando um UITableView é construído. 1) viewDidLoad é disparado primeiro. Isso indica que o UITableView está carregado na memória. 2) viewWillAppear é o próximo a disparar. Isso indica que o UITableView será exibido, mas não necessariamente que todos os objetos UITableViewCell visíveis terão a renderização totalmente instanciada / concluída.
Kennethmac2000,
2
E tudo o que foi dito acima é verdade, a questão então é: quais opções não hacky temos para descobrir quando o UITableView obteve informações suficientes de sua fonte de dados para garantir que uma solicitação de rolagem (ou seja, um scrollRectToVisible: animated: call ) realmente funcionará (e não apenas não fará nada)?
Kennethmac2000
1

Parece que você deseja atualizar o conteúdo da célula, mas sem os saltos repentinos que podem acompanhar as inserções e exclusões de células.

Existem vários artigos sobre como fazer isso. Isso é um.

Eu sugiro usar setContentOffset: animated: em vez de scrollRectToVisible: animated: para configurações perfeitas de pixel de uma visualização de rolagem.

Walt Sellers
fonte
1

Você pode tentar a seguinte lógica:

-(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 antes de chamar reloadData, defina prevIndexPath como nil. Gostar:

prevIndexPath = nil;
[mainTableView reloadData];

Testei com NSLogs, e essa lógica parece ok. Você pode personalizar / melhorar conforme necessário.

Joe M
fonte
0

finalmente fiz meu código funcionar com isso -

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

havia poucas coisas que precisavam ser cuidadas -

  1. chame-o dentro de " - (UITableViewCell *)MyTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath"
  2. apenas certifique-se de que a mensagem "scrollToRowAtIndexPath" seja enviada para a instância relevante de UITableView, que é definitivamente MyTableview neste caso.
  3. No meu caso, UIView é a visão que contém uma instância de UITableView
  4. Além disso, isso será chamado para cada carga de célula. Portanto, coloque uma lógica dentro de "cellForRowAtIndexPath" para evitar chamar "scrollToRowAtIndexPath" mais de uma vez.
smile.al.d.way
fonte
e apenas certifique-se de que esta peça não seja chamada mais de uma vez. caso contrário, bloqueia a rolagem.
smile.al.d.way
Isso pode funcionar, mas definitivamente não é elegante e não é a solução que procuro. Infelizmente, não parece haver uma maneira melhor de determinar se -reloadData realmente terminou de obter os dados ...
Josh Brown
0

Você pode redimensionar o tableview ou definir o tamanho do conteúdo neste método quando todos os dados forem carregados:

- (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);
}
Banqueiro Mittal
fonte
0

Acabei de executar o cronômetro programado de repetição e invalidá-lo apenas quando o contentSize da tabela for maior quando a altura da tableHeaderView (significa que há conteúdo de linhas na tabela). O código em C # (monotouch), mas espero que a ideia seja clara:

    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
fonte
0

Não é UITableView layoutSubviewschamado pouco antes de a visualização da tabela exibir seu conteúdo? Percebi que ele é chamado assim que a table view termina de carregar seus dados, talvez você deva investigar nessa direção.

Eric MORAND
fonte
0

Desde o iOS 6 em diante, o UITableviewmétodo delegado chamou:

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

será executado assim que sua tabela for recarregada com sucesso. Você pode fazer a personalização conforme necessário neste método.

Bharath Vankireddy
fonte
0

A melhor solução que encontrei em Swift

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

Por que não apenas estender?

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

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

role até o final:

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

Não testado com muitos dados

Andrii Tishchenko
fonte