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.