Como saber quando UITableView rolou para baixo no iPhone


100

Gostaria de saber quando UITableViewfoi rolar para baixo a fim de carregar e mostrar mais conteúdo, algo como um delegado ou outra coisa para informar o controlador quando a tabela rolar para baixo.

Alguém sabe disso, por favor me ajude, desde já agradeço!

Respostas:


18

A melhor maneira é testar um ponto na parte inferior da tela e usar esta chamada de método sempre que o usuário rolar ( scrollViewDidScroll):

- (NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point

Teste um ponto próximo à parte inferior da tela e, em seguida, usando o indexPath que ele retorna, verifique se esse indexPath é a última linha e, se for, adicione linhas.


Infelizmente, meu aplicativo atualizará os dados em tempo real para as linhas existentes nesta tabela, então, desta forma, pode obter mais conteúdo quando a tabela não estiver rolando.
Son Nguyen

247

no delegado tableview faça algo assim

ObjC:

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
    CGPoint offset = aScrollView.contentOffset;
    CGRect bounds = aScrollView.bounds;
    CGSize size = aScrollView.contentSize;
    UIEdgeInsets inset = aScrollView.contentInset;
    float y = offset.y + bounds.size.height - inset.bottom;
    float h = size.height;
    // NSLog(@"offset: %f", offset.y);   
    // NSLog(@"content.height: %f", size.height);   
    // NSLog(@"bounds.height: %f", bounds.size.height);   
    // NSLog(@"inset.top: %f", inset.top);   
    // NSLog(@"inset.bottom: %f", inset.bottom);   
    // NSLog(@"pos: %f of %f", y, h);

    float reload_distance = 10;
    if(y > h + reload_distance) {
        NSLog(@"load more rows");
    }
}

Rápido:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let offset = scrollView.contentOffset
    let bounds = scrollView.bounds
    let size = scrollView.contentSize
    let inset = scrollView.contentInset
    let y = offset.y + bounds.size.height - inset.bottom
    let h = size.height
    let reload_distance:CGFloat = 10.0
    if y > (h + reload_distance) {
        print("load more rows")
    }
}

Boa! Basta usar: y> = h + reload_distance, que é realmente uma preocupação apenas quando reload_distance = 0 (por exemplo, para saltos NÃO).
Chris Prince de

4
Este código em "scrollViewDidScroll" é executado toda vez que o usuário move o dedo para cima / para baixo na tela. É melhor movê-lo para "scrollViewDidEndDecelerating". Desta forma, ele será executado uma vez quando o usuário parar de mover o dedo.
Misha

hmm .. este código ainda funciona? não obtendo o fundo do UITableView para mim. Eu gostaria de saber o raciocínio por trás de todo o código acima, então talvez eu possa corrigir
FlowUI. SimpleUITesting.com

offset: 237,000000 content.height: 855,000000 bounds.height: 667,000000 inset.top: 64,000000 inset.bottom: 49,000000 pos: 855,000000 de 855,000000 se está ficando falso
Abhishek Thapliyal

62

Neoneyes modificados respondem um pouco.

Esta resposta é direcionada àqueles que desejam que o evento seja acionado apenas uma vez a cada liberação do dedo .

Adequado ao carregar mais conteúdo de algum provedor de conteúdo (serviço da web, dados básicos, etc.). Observe que essa abordagem não respeita o tempo de resposta de seu serviço da web.

- (void)scrollViewDidEndDragging:(UIScrollView *)aScrollView
                  willDecelerate:(BOOL)decelerate
{
    CGPoint offset = aScrollView.contentOffset;
    CGRect bounds = aScrollView.bounds;
    CGSize size = aScrollView.contentSize;
    UIEdgeInsets inset = aScrollView.contentInset;
    float y = offset.y + bounds.size.height - inset.bottom;
    float h = size.height;

    float reload_distance = 50;
    if(y > h + reload_distance) {
        NSLog(@"load more rows");
    }
}

Isso não funciona. Sempre que o usuário rola para baixo, o "carregar mais linhas" é chamado. Isso acontece mesmo quando o usuário já rolou para o fundo e está esperando a API retornar os dados.
Dean

2
Dean, a questão era saber quando a exibição da mesa rolou para o fundo. Se você está obtendo dados de uma API ou não, é irrelevante, não é?
Eyeball

Mas não há mensagem mostrando load more .. para que o usuário saiba que há mais resultados a serem mostrados.
iPhone 7 de

Boa resposta no meu caso está funcionando se: float reload_distance = 10; if (y> (h - reload_distance)) {NSLog (@ "carregar mais linhas"); } porque y = 565 e h também é 565
Abhishek Thapliyal

1
Tentei as respostas de Eyeball e @neoneye. Acredite ou não, 'scrollViewDidScroll' chamado várias vezes em comparação com 'scrollViewDidEndDragging'. Portanto, foi eficiente usar 'scrollViewDidEndDragging' porque tenho uma chamada de API nele.
Vinod Supnekar de

39

adicione este método no UITableViewDelegate:

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{   
    CGFloat height = scrollView.frame.size.height;

    CGFloat contentYoffset = scrollView.contentOffset.y;

    CGFloat distanceFromBottom = scrollView.contentSize.height - contentYoffset;

    if(distanceFromBottom < height) 
    {
        NSLog(@"end of the table");
    }
}

3
Eu gosto dessa solução, exceto por um pequeno detalhe. if (distanceFromBottom <= height)
Mark McCorkle

Isso me ajudou a solucionar meu problema, obrigado !! uma barra de guias !! era uma barra de guia estúpida !!
Dmytro

Isso funciona como um encanto e também é simples, mas estou enfrentando um pequeno problema, como se estivesse executando duas ou três vezes na última linha. como fazê-lo executar apenas uma vez ao atingir a última linha do tableview?
R. Mohan

Muito obrigado!! Eu coloquei este código em scrollViewWillBeginDecelerating e ele executa apenas uma vez quando rolado para a última linha.
Manali

20

Nenhuma das respostas acima me ajudou, então eu vim com o seguinte:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)aScrollView
{   
    NSArray *visibleRows = [self.tableView visibleCells];
    UITableViewCell *lastVisibleCell = [visibleRows lastObject];
    NSIndexPath *path = [self.tableView indexPathForCell:lastVisibleCell];
    if(path.section == lastSection && path.row == lastRow)
    {
        // Do something here
    }
}

Você pode explicar de onde vem "lastSection"?
ainesophaur

isto é para visualização de rolagem e não tabela
iosMentalist

19

Use – tableView:willDisplayCell:forRowAtIndexPath:( UITableViewDelegatemétodo)

Basta comparar o indexPathcom os itens em sua matriz de dados (ou qualquer fonte de dados que você usa para sua exibição de tabela) para descobrir se o último elemento está sendo exibido.

Documentos: http://developer.apple.com/library/ios/documentation/uikit/reference/UITableViewDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/UITableViewDelegate/tableView:willDisplayCell:forRowAtIndexPath :


Então, se você recarregar os dados de tableView ', você ficará preso em um loop infinito.
Dielson Sales de

14

UITableViewé uma subclasse de UIScrollViewe UITableViewDelegateestá em conformidade com UIScrollViewDelegate. Portanto, o delegado que você anexa à visualização da tabela obterá eventos como scrollViewDidScroll:, e você pode chamar métodos como contentOffsetna visualização da mesa para encontrar a posição de rolagem.


Sim, é isso que estou procurando, ao implementar esse delegado e verificar se a posição de rolagem é UITableViewScrollPositionBottom, saberei quando a tabela for rolada para baixo, muito obrigado
Son Nguyen

8
NSLog(@"%f / %f",tableView.contentOffset.y, tableView.contentSize.height - tableView.frame.size.height);

if (tableView.contentOffset.y == tableView.contentSize.height - tableView.frame.size.height)
        [self doSomething];

Bom e simples


8

em Swiftvocê pode fazer algo assim. A seguinte condição será verdadeira toda vez que você chegar ao final do tableview

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
        if indexPath.row+1 == postArray.count {
            println("came to last row")
        }
}

1
o problema é quando tableview tem apenas 3 células, por exemplo, println("came to last row")este código é executado!
Mc.Lover

@Mc.Lover, esse era o problema exato para mim, pois o uso da CPU estava indo para 92%. a resposta de neoneye funcionou para mim. Eu também adicionei a versão rápida para ele, se necessário.
Anuran Barman

7

Com base na resposta de @Jay Mayu, que considero uma das melhores soluções:

Objective-C

// UITableViewDelegate
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {

// Need to call the service & update the array
if(indexPath.row + 1 == self.sourceArray.count) {
    DLog(@"Displayed the last row!");
  }
}

Swift 2.x

// UITableViewDelegate
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    if (indexPath.row + 1) == sourceArray.count {
        print("Displayed the last row!")
    }
}

5

Eu geralmente uso isso para carregar mais dados, quando a última célula começa a exibir

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
   if (indexPath.row ==  myDataArray.count-1) {
        NSLog(@"load more");
    }
}

1
isto está errado. Cada vez que você chamar reloadData, uma solicitação será feita. E às vezes você só precisa recarregar a mesa.
Patrick Bassut,

você queria dizer, se você está na última célula e depois recarrega os dados, isso vai acontecer?
Shaik Riyaz

5

Pegando novas respostas excelentes, mas rapidamente, e renomeando as variáveis.

Basicamente, sabemos que atingimos a parte inferior da visualização da tabela se yOffsetAtBottomestiver além da altura do conteúdo da tabela.

func isTableViewScrolledToBottom() -> Bool {
    let tableHeight = tableView.bounds.size.height
    let contentHeight = tableView.contentSize.height
    let insetHeight = tableView.contentInset.bottom

    let yOffset = tableView.contentOffset.y
    let yOffsetAtBottom = yOffset + tableHeight - insetHeight

    return yOffsetAtBottom > contentHeight
}

5

Aqui está o código da versão 3.0 do swift.

   func scrollViewDidScroll(_ scrollView: UIScrollView) {

        let offset = scrollView.contentOffset
        let bounds = scrollView.bounds
        let size = scrollView.contentSize
        let inset = scrollView.contentInset
        let y: Float = Float(offset.y) + Float(bounds.size.height) + Float(inset.bottom)
        let height: Float = Float(size.height)
        let distance: Float = 10

        if y > height + distance {
            // load more data
        }
    }

3

Minha solução é adicionar células antes que tableview desacelere no deslocamento estimado. É previsível pela velocidade.

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)offset {

    NSLog(@"offset: %f", offset->y+scrollView.frame.size.height);
    NSLog(@"Scroll view content size: %f", scrollView.contentSize.height);

    if (offset->y+scrollView.frame.size.height > scrollView.contentSize.height - 300) {
        NSLog(@"Load new rows when reaches 300px before end of content");
        [[DataManager shared] fetchRecordsNextPage];
    }
}

3

Atualização para Swift 3

A resposta de Neoneye funcionou melhor para mim no Objetivo C, isso é o equivalente à resposta no Swift 3:

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    let offset: CGPoint = scrollView.contentOffset
    let bounds: CGRect = scrollView.bounds
    let size: CGSize = scrollView.contentSize
    let inset: UIEdgeInsets = scrollView.contentInset
    let y: CGFloat = offset.y + bounds.size.height - inset.bottom
    let h: CGFloat = size.height
//        print("offset: %f", offset.y)
//        print("content.height: %f", size.height)
//        print("bounds.height: %f", bounds.size.height)
//        print("inset.top: %f", inset.top)
//        print("inset.bottom: %f", inset.bottom)
//        print("position: %f of %f", y, h)

    let reloadDistance: CGFloat = 10

    if (y > h + reloadDistance) {
            print("load more rows")
    }
}

2

Quero realizar alguma ação em qualquer 1 Tableviewcell completa.

Portanto, o código é o link:

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    NSArray* cells = self.tableView.visibleCells;
    NSIndexPath *indexPath = nil ;

    for (int aIntCount = 0; aIntCount < [cells count]; aIntCount++)
    {

        UITableViewCell *cell = [cells objectAtIndex:aIntCount];
CGRect cellRect = [self.tableView convertRect:cell.frame toView:self.tableView.superview];

        if (CGRectContainsRect(self.tableView.frame, cellRect))
        {
            indexPath = [self.tableView indexPathForCell:cell];

    //  remain logic
        }
     }
}

Que isso seja útil para alguém.


0

A resposta de @neoneye funcionou para mim. Aqui está a versão do Swift 4

    let offset = scrollView.contentOffset
    let bounds = scrollView.bounds
    let size = scrollView.contentSize
    let insets = scrollView.contentInset
    let y = offset.y + bounds.size.height - insets.bottom
    let h = size.height
    let reloadDistance = CGFloat(10)
    if y > h + reloadDistance {
        //load more rows
    }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.