Existe alguma maneira de descobrir quando um UITableView
terminou de solicitar dados de sua fonte de dados?
Nenhum dos métodos viewDidLoad
/ viewWillAppear
/ viewDidAppear
do 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 reloadData
em viewDidAppear
, uma vez que, quando reloadData
retorna, 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 reloadData
chamada) 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
, viewWillAppear
ou viewDidAppear
).
fonte
Respostas:
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
'sreloadData
é a melhor abordagem:reloadData
não termina antes que a tabela termine de recarregar seus dados. Portanto, quando o segundoNSLog
é disparado, a visualização da tabela acabou de solicitar os dados.Criei uma subclasse
UITableView
para enviar métodos ao delegado antes e depoisreloadData
. Ele funciona como um encanto.fonte
reloadData
retorna imediatamente e vejo "END reloadData" antes que as células sejam realmente recarregadas (ou seja, antes que osUITableViewDataSource
métodos sejam chamados). Minha experimentação demonstra exatamente o oposto do que você diz. Devo entender mal o que você está tentando dizer.[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 tabelareloadData
.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.
Atualização rápida:
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
fonte
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?
e...
Uso:
fonte
dispatch_async(dispatch_get_main_queue(), ^{completionBlock();});
. Isso basicamente pula os blocos postados pela visualização da tabelareloadData
.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.fonte
Essa é a minha solução. 100% funciona e é usado em muitos projetos. É uma subclasse UITableView simples.
É semelhante à solução de Josh Brown, com uma exceção. Nenhum atraso é necessário no método performSelector. Não importa o quanto
reloadData
demore.tableViewDidLoadData:
sempre dispara quandotableView
acaba de perguntardataSource
cellForRowAtIndexPath
.Mesmo se você não quiser uma subclasse,
UITableView
você 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 parareloadData
:Aproveitar. :)
fonte
performSelector
ou a execução no thread principaldispatch_asynch
não funcionam no iOS 9 .Esta é uma resposta a uma pergunta um pouco diferente: eu precisava saber quando
UITableView
também havia terminado de ligarcellForRowAtIndexPath()
. EulayoutSubviews()
criei uma subclasse (obrigado @Eric MORAND) e adicionei um retorno de chamada delegado:SDTableView.h:
SDTableView.m:
Uso:
MyTableViewController.h:
MyTableViewController.m:
NOTAS: Como esta é uma subclasse da
UITableView
qual já possui uma propriedade delegate apontando para,MyTableViewController
nã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
UITableView
propriedade emMyTableViewController
deve ser alterada para usar a novaSDTableView
classe. Isso é feito no Interface Builder Identity Inspector. Selecione oUITableView
interior deUITableViewController
e defina sua "Classe personalizada" comoSDTableView
.fonte
Eu tinha encontrado algo semelhante para receber notificação de mudança
contentSize
deTableView
. Acho que deve funcionar aqui também, já que contentSize também muda com o carregamento de dados.Experimente isto:
Por
viewDidLoad
escrito,e adicione este método ao seu viewController:
Você pode precisar de pequenas modificações na verificação de mudança. Isso funcionou para mim, no entanto.
Felicidades! :)
fonte
-viewWillAppear
e remova-se do-viewWillDisapear
método.Aqui está uma solução possível, embora seja um hack:
Onde seu
-scrollTableView
mé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 ...fonte
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.
fonte
-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?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.
fonte
Você pode tentar a seguinte lógica:
E antes de chamar reloadData, defina prevIndexPath como nil. Gostar:
Testei com NSLogs, e essa lógica parece ok. Você pode personalizar / melhorar conforme necessário.
fonte
finalmente fiz meu código funcionar com isso -
havia poucas coisas que precisavam ser cuidadas -
- (UITableViewCell *)MyTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
"fonte
Você pode redimensionar o tableview ou definir o tamanho do conteúdo neste método quando todos os dados forem carregados:
fonte
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:
fonte
Não é
UITableView
layoutSubviews
chamado 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.fonte
Desde o iOS 6 em diante, o
UITableview
método delegado chamou:será executado assim que sua tabela for recarregada com sucesso. Você pode fazer a personalização conforme necessário neste método.
fonte
A melhor solução que encontrei em Swift
fonte
Por que não apenas estender?
role até o final:
Não testado com muitos dados
fonte