Alterar o comportamento de rolagem padrão do cabeçalho da seção UITableView

147

Eu tenho um UITableView com duas seções. É uma visão simples da tabela. Estou usando o viewForHeaderInSection para criar modos de exibição personalizados para esses cabeçalhos. Por enquanto, tudo bem.

O comportamento de rolagem padrão é que, quando uma seção é encontrada, o cabeçalho da seção permanece ancorado abaixo da barra de navegação até que a próxima seção seja exibida.

Minha pergunta é a seguinte: posso alterar o comportamento padrão para que os cabeçalhos da seção NÃO fiquem ancorados na parte superior, mas role pela barra de navegação com o restante das linhas da seção?

Estou perdendo algo óbvio?

Obrigado.

davidjhinson
fonte
1
Essa deve ser uma das melhores perguntas escritas neste site! :) Obrigado.
Fattie
1
Verifique aqui a maneira correta de obter esse efeito stackoverflow.com/a/13735238/1880899
Sumit Anantwar 28/11

Respostas:

175

A maneira que resolvi esse problema é ajustar o contentOffsetacordo com o contentInsetno UITableViewControllerDelegate(estende UIScrollViewDelegate) assim:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
       CGFloat sectionHeaderHeight = 40;
   if (scrollView.contentOffset.y<=sectionHeaderHeight&&scrollView.contentOffset.y>=0) {
       scrollView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0);
   } else if (scrollView.contentOffset.y>=sectionHeaderHeight) {
       scrollView.contentInset = UIEdgeInsetsMake(-sectionHeaderHeight, 0, 0, 0);
   }
}

O único problema aqui é que ele perde um pouco de força ao rolar de volta para o topo.


{OBSERVAÇÃO: O "40" deve ter a altura exata do SEU cabeçalho da seção 0. Se você usar um número maior que a altura do cabeçalho da seção 0, verá que a sensação do dedo é afetada (tente "1000" e verá que o comportamento do salto é meio estranho no topo). se o número corresponder à altura do cabeçalho da seção 0, a sensação do dedo parecerá perfeita ou quase perfeita.}

embeber
fonte
1
Solução perfeita do que a acima.
Prathumca
4
Obrigado! Esta é uma solução perfeita. Você tem a solução para o rodapé da seção?
Tuan Nguyen
12
Esta solução não manipula a barra de toque corretamente, que deveria rolar para o topo da lista.
Reid Ellis
se vc precisa o mesmo comportamento para vista para o seção de rodapé apenas mudar a última linha como esta: scrollView.contentInset = UIEdgeInsetsMake (0, 0, sectionHeaderHeight, 0)
alex
Isso parece ser suficiente: scrollView.contentInset = UIEdgeInsetsMake (scrollView.contentOffset.y> = 0? -ScrollView.contentOffset.y: 0, 0, 0, 0);
MacMark 14/02
85

Você também pode adicionar uma seção com zero linhas na parte superior e simplesmente usar o rodapé da seção anterior como cabeçalho da próxima.

voidStern
fonte
13
No meu caso onde eu tenho mais de 2 seções, o rodapé irá âncoras no fundo ..
Joseph Lin
3
Isso é criativo, mas você precisa ajustar o indexPath se estiver usando um NSFetchedResultsController para carregar a tabela.
XJones
+1 de longe a melhor solução - levei três linhas para implementar!
24412 Jon
Brilliant Brilliant Brilliant :-)
iphonic
Como isso funcionaria para o rodapé que ancora? Gostaria de receber alguma ajuda! Obrigado
darwindeeds
36

Se eu fizesse isso, aproveitaria o fato de que UITableViews no estilo Plain tem os cabeçalhos fixos e os no estilo Agrupado não. Provavelmente, pelo menos, tentaria usar uma célula de tabela personalizada para imitar a aparência de células simples em uma tabela agrupada.

Na verdade, eu não tentei isso, por isso pode não funcionar, mas é o que eu sugiro fazer.

Colin Barrett
fonte
Provavelmente funcionaria, mas já foi tentado? E parece que dá muito trabalho quando a resposta do @ voidStern funciona perfeitamente !!
bentford
A resposta do voidStern não funciona para mim quando tenho mais de duas seções. A resposta de Colin é mais simples / elegante, sem a necessidade de alterar manualmente o número da seção e adicionar a contagem da seção.
Joseph Lin
Dicas: use tableView.separatorColor = [UIColor clearColor];para imitar uma exibição de tabela simples.
Joseph Lin
3
Tentei fazer isso, mas quando não conseguia fazer com que as células da tabela agrupada se parecessem com as células simples, ou seja, remova as margens esquerda e direita de ambos os lados das células. Como você fez isso?
Adam Carter
1
Muito obrigado por esclarecer as coisas sobre o UITableViewStyle, ou seja, agrupadas e simples, que decidem se o cabeçalho / rodapé da seção do tableview ficará fixo ou não. Obrigado, muito obrigado @Colin Barrett
Dhaval H. Nena
28

Sei que chega tarde, mas encontrei a solução definitiva!

O que você deseja fazer é se você tiver 10 seções, deixe o dataSource retornar 20. Use números pares para cabeçalhos de seção e números ímpares para o conteúdo da seção. algo assim

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (section%2 == 0) {
        return 0;
    }else {
        return 5;
    }
}

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    if (section%2 == 0) {
        return [NSString stringWithFormat:@"%i", section+1];
    }else {
        return nil;
    }
}

Voilá! : D

LocoMike
fonte
boa ideia. mas se você tiver títulos de índice de seção, suspeito que isso os atrapalhe.
roocell
porque? você pode fazer o mesmo com os títulos das seções. Lembre-se de nulo para números ímpares e um título para o número par.
LocoMike 12/01
1
sim - esse método funciona muito bem. Foi muita contabilidade (provavelmente relacionada ao modo como tenho os títulos das seções), mas funcionou. O truque é que numberOfRowsInSection e cellForRowAtIndexPath usam if (seção% 2! = 0 && section! = 0), enquanto o titleForHeaderInSection e as outras funções de índice de seção usam if (section% 2 == 0)
roocell
o remetente deve escolher isso como resposta. Seria ótimo se houvesse uma maneira de subclassificar o uitableview para ancorar os títulos das seções automaticamente.
roocell
Isso funcionou melhor para mim. Tentei primeiro a solução da @ Colin, mas ela não funcionou bem com o altamente personalizado UITableViewque estou usando.
Kubi 13/03/12
15

Há várias coisas que precisam ser feitas para resolver esse problema de maneira não hacky:

  1. Defina o estilo de exibição da tabela como UITableViewStyleGrouped
  2. Defina a visualização da tabela backgroundColorcomo[UIColor clearColor]
  3. Defina a backgroundViewcélula de exibição em cada tabela como uma exibição vazia combackgroundColor [UIColor clearColor]
  4. Se necessário, defina a visualização da tabela rowHeightadequadamente ou substitua tableView:heightForRowAtIndexPath:se as linhas individuais tiverem alturas diferentes.
Neil Gall
fonte
(+1) @Neil, tentei sua resposta, mas infelizmente UITableViewStyleGroupedas células da tabela possuem margens extras que alteram seu alinhamento com o cabeçalho. Esse é um problema quando você realmente deseja representar uma tabela com várias colunas e usar o cabeçalho para mostrar os títulos das colunas (e depois alinhar as entradas individuais da tabela com esses títulos).
brainjam
15

Originalmente publicado Aqui , uma solução rápida usando o IB. O mesmo pode ser feito programaticamente, embora de maneira simples.

Uma maneira provavelmente mais fácil de conseguir isso (usando o IB):

Arraste um UIView para o TableView para torná-lo sua visualização de cabeçalho.

  1. Defina a altura da visualização do cabeçalho como 100px
  2. Defina o contentInset (superior) da tableview para -100
  3. Os cabeçalhos de seção agora rolam como qualquer célula normal.

Algumas pessoas comentaram dizendo que esta solução oculta o primeiro cabeçalho, no entanto, eu não percebi esse problema. Funcionou perfeitamente para mim e foi de longe a solução mais simples que vi até agora.

Doc
fonte
Observe que você provavelmente deve definir a exibição para usar uma cor de plano de fundo de cor clara, caso contrário, se você rolar para cima, verá branco no salto.
Doc
3
Ele faz peles meu primeiro cabeçalho
Leena
Leena, em qual versão do iOS você está executando? A visualização é um UITableViewController ou um UIView com um UITableView? Não sei por que, para algumas pessoas, essa solução oculta o primeiro cabeçalho, por isso estou tentando encontrar alguma discrepância.
Doc
Esta solução é perfeita. Estou procurando uma infinita aparência de fundo na parte superior e inferior da célula - isso é totalmente possível. Agradável!
Taylor Halliday
Esta solução é excelente. Mais fácil de realizar do que as outras respostas neste tópico. Na minha opinião, esta deve ser a resposta aceita.
John Rogers
15

Eu não estava satisfeito com as soluções descritas aqui até agora, então tentei combiná-las. O resultado é o seguinte código, inspirado em @awulf e @cescofry. Funciona para mim porque não tenho cabeçalho de exibição de tabela real. Se você já possui um cabeçalho de exibição de tabela, pode ser necessário ajustar a altura.

// Set the edge inset
self.tableView.contentInset = UIEdgeInsetsMake(-23.0f, 0, 0, 0);

// Add a transparent UIView with the height of the section header (ARC enabled)
[self.tableView setTableHeaderView:[[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 23.0f)]];
Thomas Kekeisen
fonte
Esta foi uma solução mais simples que encontrei.
Zorayr 25/10
Isso funcionou para mim no iOS9.2, portanto, apesar de uma resposta mais antiga, ainda é válida. E não ocultou o cabeçalho da primeira seção. Funciona e funciona perfeitamente.
idStar 30/01
Perfeito. Se o seu tableview já tem um cabeçalho, apenas aumentar a sua altura por constante top restrição o deslocamento em seguida, aumentar o conteúdo do cabeçalho para mudar o conteúdo para a posição original
Jason
14

Basta alterar o estilo do TableView:

self.tableview = [[UITableView alloc] initwithFrame:frame style:UITableViewStyleGrouped];

Documentação UITableViewStyle :

UITableViewStylePlain- Uma exibição de tabela simples. Quaisquer cabeçalhos ou rodapés de seção são exibidos como separadores em linha e flutuam quando a exibição da tabela é rolada.

UITableViewStyleGrouped- Uma exibição de tabela cujas seções apresentam grupos distintos de linhas. Os cabeçalhos e rodapés da seção não flutuam.

Arthur S
fonte
ótima resposta, funcionou para mim. não há necessidade de qualquer trabalho em torno
Shams Ahmed
9

Selecione o estilo TableView agrupado

Selecione o estilo de Visualização de tabela agrupada no Inspetor de atributos do tableView no storyboard.

Arjun Shukla
fonte
5

Defina o headerView da tabela com uma vista transparente com a mesma altura do cabeçalho na vista de seção. Também inicie a visualização da tabela com um quadro na altura.

self.tableview = [[UITableView alloc] initWithFrame:CGRectMake(0, - height, 300, 400)];

UIView *headerView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, width, height)] autorelease];
[self.tableView setTableHeaderView:headerView];
cescofry
fonte
4

Encontrei uma solução alternativa, use a primeira célula de cada seção em vez de uma seção de cabeçalho real; essa solução não parece tão limpa, mas funciona tão bem que você pode usar uma célula de protótipo definida para a seção de cabeçalhos e no método cellForRowAtIndexPath solicite o indexPath.row == 0, se verdadeiro, use a célula protótipo da seção do cabeçalho; caso contrário, use a célula protótipo padrão.

AlexBB
fonte
Se você ainda deseja trabalhar com indexPath.row& indexPath.section, certifique-se de retornar uma altura de 0.0fpara - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)sectionque seus cabeçalhos fiquem invisíveis.
Sopa
4

Altere seu estilo do TableView:

self.tableview = [[UITableView alloc] initwithFrame:frame style:UITableViewStyleGrouped];

De acordo com a documentação da Apple para o UITableView:

UITableViewStylePlain- Uma exibição de tabela simples. Quaisquer cabeçalhos ou rodapés de seção são exibidos como separadores em linha e flutuam quando a exibição da tabela é rolada.

UITableViewStyleGrouped- Uma exibição de tabela cujas seções apresentam grupos distintos de linhas. Os cabeçalhos e rodapés da seção não flutuam. Espero que esta pequena mudança o ajude.

Aks
fonte
2

Agora que o estilo agrupado parece basicamente o mesmo do iOS 7 (em termos de nivelamento e plano de fundo), para nós, a melhor e mais fácil (ou seja, menos hacky) correção foi simplesmente mudar o estilo da visualização da tabela para agrupado. Fazer jack com contentInsets sempre foi um problema quando integramos uma barra de navegação com rolagem na parte superior. Com um estilo de exibição de tabela agrupada, parece exatamente o mesmo (com nossas células) e os cabeçalhos de seção permanecem fixos. Sem estranheza de rolagem.

Greg Combs
fonte
Esta é a melhor solução, especialmente se as células forem personalizadas, parece simplesmente interromper a "retenção" do cabeçalho da seção quando a rolagem atinge o topo.
Aço reciclado
1

Atribua uma inserção negativa ao seu tableView. Se você tem cabeçalhos de seção alta de 22px e não deseja que eles fiquem presos, logo após recarregar os dados, adicione:

self.tableView.contentInset = UIEdgeInsetsMake(-22, 0, 0, 0); 
self.tableView.contentSize = CGSizeMake(self.tableView.contentSize.width, self.tableView.contentSize.height+22); 

Funciona como um encanto para mim. Também funciona para rodapés de seção, basta atribuir a inserção negativa na parte inferior.

Mike A
fonte
Isso apenas corta o primeiro cabeçalho?
Cannyboy
Trabalhou para mim perfeitamente !! Obrigado
Daniel
0

Eu adiciono a tabela a uma exibição de rolagem e isso parece funcionar bem.

Dolbex
fonte
0

Confira minha resposta aqui . Essa é a maneira mais fácil de implementar os cabeçalhos de seção não flutuantes sem hacks.

Sumit Anantwar
fonte
0

A resposta da @ LocoMike melhor se ajustou ao meu tableView, no entanto, ela quebrou ao usar rodapés também. Portanto, esta é a solução corrigida ao usar cabeçalhos e rodapés:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return (self.sections.count + 1) * 3;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (section % 3 != 1) {
        return 0;
    }
    section = section / 3;
    ...
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    if (section % 3 != 0) {
        return nil;
    }
    section = section / 3;
    ...
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    if (section % 3 != 0) {
        return 0;
    }
    section = section / 3;
    ...
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
    if (section % 3 != 2) {
        return 0;
    }
    section = section / 3;
    ...
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    if (section % 3 != 0) {
        return nil;
    }
    section = section / 3;
    ...
}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
    if (section % 3 != 2) {
        return nil;
    }
    section = section / 3;
    ...
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    int section = indexPath.section;
    section = section / 3;
    ...
}
marmor
fonte
0

Versão rápida da resposta @awulf, que funciona muito bem!

func scrollViewDidScroll(scrollView: UIScrollView) {
    let sectionHeight: CGFloat = 80
    if scrollView.contentOffset.y <= sectionHeight {
        scrollView.contentInset = UIEdgeInsetsMake( -scrollView.contentOffset.y, 0, 0, 0)
    }else if scrollView.contentOffset.y >= sectionHeight {
        scrollView.contentInset = UIEdgeInsetsMake(-sectionHeight, 0, 0, 0)
    }
}
Jeff
fonte
-2

Aprendi que apenas a configuração da propriedade tableHeaderView faz isso, ou seja:

 tableView.tableHeaderView = customView;

e é isso.

Soni
fonte