Estou tentando rolar para a parte inferior de um UITableView após concluir a execução [self.tableView reloadData]
Eu originalmente tinha
[self.tableView reloadData]
NSIndexPath* indexPath = [NSIndexPath indexPathForRow: ([self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1) inSection: ([self.tableView numberOfSections]-1)];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
Mas então eu li que reloadData é assíncrono, então a rolagem não acontece desde o self.tableView
, [self.tableView numberOfSections]
e [self.tableView numberOfRowsinSection
são todos 0.
Obrigado!
O estranho é que estou usando:
[self.tableView reloadData];
NSLog(@"Number of Sections %d", [self.tableView numberOfSections]);
NSLog(@"Number of Rows %d", [self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1);
No console, ele retorna Seções = 1, Linha = -1;
Quando eu faço exatamente o mesmo NSLogs cellForRowAtIndexPath
, recebo Sections = 1 e Row = 8; (8 está certo)
Respostas:
A recarga acontece durante a próxima passagem de layout, o que normalmente acontece quando você retorna o controle ao loop de execução (depois, digamos, da ação do botão ou do que for retornado).
Portanto, uma maneira de executar algo depois que a exibição da tabela é recarregada é simplesmente forçar a exibição da tabela a executar o layout imediatamente:
Outra maneira é agendar seu código pós-layout para execução posterior usando
dispatch_async
:ATUALIZAR
Após uma investigação mais aprofundada, acho que a exibição da tabela envia
tableView:numberOfSections:
etableView:numberOfRowsInSection:
para sua fonte de dados antes de retornarreloadData
. Se o delegado implementartableView:heightForRowAtIndexPath:
, a exibição da tabela também enviará isso (para cada linha) antes de retornar dereloadData
.No entanto, a exibição da tabela não envia
tableView:cellForRowAtIndexPath:
outableView:headerViewForSection
até a fase de layout, que ocorre por padrão quando você retorna o controle ao loop de execução.Também acho que, em um pequeno programa de teste, o código na sua pergunta rola corretamente para a parte inferior da exibição da tabela, sem que eu faça nada de especial (como enviar
layoutIfNeeded
ou usardispatch_async
).fonte
dispatch_async(dispatch_get_main_queue())
não é garantido que o método funcione. Estou vendo um comportamento não determinístico com ele, no qual, às vezes, o sistema conclui as layoutSubviews e a renderização da célula antes do bloco de conclusão, e às vezes depois. Vou postar uma resposta que funcionou para mim abaixo.dispatch_async(dispatch_get_main_queue())
nem sempre trabalhar. Vendo resultados aleatórios aqui.NSRunLoop
. Um loop de execução tem fases diferentes e você pode agendar um retorno de chamada para uma fase específica (usando aCFRunLoopObserver
). O layout do cronograma do UIKit ocorre durante uma fase posterior, após o retorno do manipulador de eventos.Rápido:
Objetivo-C:
fonte
tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
método Mock View Controller o método e inserindo em minha substituição o que quisesse notificar que a recarga havia terminado.No Xcode 8.2.1, iOS 10 e swift 3,
Você pode determinar o final
tableView.reloadData()
facilmente usando um bloco CATransaction:O exemplo acima também funciona para determinar o final de reloadData () do UICollectionView e reloadAllComponents () do UIPickerView.
fonte
beginUpdates
eendUpdates
chamadas.setCompletionBlock
meusnumberOfSections
shows 2 ... até agora tudo bem. No entanto, se por dentrosetCompletionBlock
eu façotableView.headerView(forSection: 1)
isso retornanil
!!! portanto, acho que esse bloco está acontecendo antes da recarga ou captura algo antes ou estou fazendo algo errado. Para sua informação, tentei a resposta de Tyler e funcionou! @FattieNão
dispatch_async(dispatch_get_main_queue())
é garantido que o método acima funcione . Estou vendo um comportamento não determinístico com ele, no qual, às vezes, o sistema conclui as layoutSubviews e a renderização da célula antes do bloco de conclusão, e às vezes depois.Aqui está uma solução que funciona 100% para mim, no iOS 10. Ele requer a capacidade de instanciar o UITableView ou UICollectionView como uma subclasse personalizada. Aqui está a solução UICollectionView, mas é exatamente a mesma para o UITableView:
CustomCollectionView.h:
CustomCollectionView.m:
Exemplo de uso:
Veja aqui uma versão Swift desta resposta
fonte
layoutSubviews
ele deve ser definidonil
como chamadas subsequentes paralayoutSubviews
, não necessariamente devido àreloadData
chamada, resultará na execução do bloco, pois há uma forte referência sendo mantida, que não é o comportamento desejado.reloadDataCompletionBlock
uma matriz de blocos e iterar sobre eles na execução e esvaziar a matriz depois disso.Eu tive os mesmos problemas que Tyler Sheaffer.
Eu implementei sua solução no Swift e resolveu meus problemas.
Swift 3.0:
Swift 2:
Exemplo de uso:
fonte
if let
dizendoreloadDataCompletionBlock?()
que irá chamar sse não nil 💥self.reloadDataCompletionBlock? { completion() }
deveria ter sidoself.reloadDataCompletionBlock?()
E uma
UICollectionView
versão, baseada na resposta da kolaworld:https://stackoverflow.com/a/43162226/1452758
Precisa de teste. Até o momento, funciona no iOS 9.2, Xcode 9.2 beta 2, com rolagem de uma coleção para um índice, como um fechamento.
Uso:
fonte
Parece que as pessoas ainda estão lendo esta pergunta e as respostas. B / c disso, estou editando minha resposta para remover a palavra síncrona, que é realmente irrelevante para isso.
When [tableView reloadData]
retorna, as estruturas de dados internas atrás do tableView foram atualizadas. Portanto, quando o método for concluído, você poderá rolar com segurança até o final. Eu verifiquei isso no meu próprio aplicativo. A resposta amplamente aceita por @ rob-mayoff, embora também seja confusa em terminologia, reconhece o mesmo em sua última atualização.Se você
tableView
não estiver rolando para baixo, pode haver um problema em outro código que você não postou. Talvez você esteja alterando os dados depois que a rolagem estiver concluída e não estiver recarregando e / ou rolando para baixo, então?Adicione algum log da seguinte maneira para verificar se os dados da tabela estão corretos depois
reloadData
. Eu tenho o código a seguir em um aplicativo de exemplo e funciona perfeitamente.fonte
reloadData
não é síncrono. Ela costumava ser - veja esta resposta: stackoverflow.com/a/16071589/193896reloadData
retorno.reloadData
. Use meu caso de testeviewWillAppear
para aceitar ascrollToRowAtIndexPath:
linha b / c que não faz sentido setableView
não for exibida. Você verá quereloadData
atualizou os dados armazenados em cache natableView
instância ereloadData
é síncrono. Se você estiver se referindo a outrostableView
métodos delegados chamados quando otableView
layout estiver sendo layout, eles não serão chamados se otableView
não for exibido. Se estou entendendo mal o seu cenário, explique.Eu uso esse truque, com certeza já o publiquei em uma duplicata desta pergunta:
fonte
Na verdade, este resolveu meu problema:
fonte
Tente desta forma, vai funcionar
Vou executar quando a tabela estiver completamente carregada
Outra solução é que você pode subclassificar UITableView
fonte
Acabei usando uma variação da solução de Shawn:
Crie uma classe UITableView personalizada com um delegado:
Então, no meu código, eu uso
Certifique-se também de definir sua exibição de tabela como CustomTableView no construtor de interfaces:
fonte
Em Swift 3.0 + podemos criar uma extensão para
UITableView
com umescaped Closure
como abaixo:E use-o como abaixo, onde quiser:
espero que isso ajude alguém. Felicidades!
fonte
Detalhes
Solução
Uso
Amostra completa
Resultados
fonte
Apenas para oferecer outra abordagem, com base na ideia de a conclusão ser a célula "última visível" a ser enviada
cellForRow
.Um possível problema é: se
reloadData()
tiver terminado antes dalastIndexPathToDisplay
definição, a célula 'last visível' será exibida antes dalastIndexPathToDisplay
definição e a conclusão não será chamada (e estará no estado 'em espera'):Se revertermos, poderemos terminar com a conclusão sendo rolada antes
reloadData()
.fonte
Tente o seguinte:
A cor do tableView será alterada de preto para verde somente após a conclusão da
reloadData()
função.fonte
Você pode usar a função performBatchUpdates do uitableview
Aqui está como você pode conseguir
fonte
Criando uma extensão reutilizável do CATransaction:
Agora, criando uma extensão do UITableView que usaria o método de extensão do CATransaction:
Uso:
fonte
Você pode usá-lo para fazer alguma coisa após recarregar os dados:
fonte
Tente definir atrasos:
fonte