É possível desabilitar headers flutuantes no UITableView com UITableViewStylePlain?

Eu estou usando um UITableView para layout de ‘páginas’ de conteúdo. Eu estou usando os headers da visão de tabela para o layout de certas imagens, etc. e eu preferiria que eles não flutuassem, mas ficassem estáticos como quando o estilo é definido como UITableViewStyleGrouped .

Outro então usando UITableViewStyleGrouped , existe uma maneira de fazer isso? Eu gostaria de evitar o uso de agrupados, pois adiciona uma margem a todas as minhas células e requer a desativação da visualização em segundo plano para cada uma das células. Eu gostaria de ter controle total do meu layout. Idealmente eles seriam um “UITableViewStyleBareBones”, mas eu não vi essa opção nos documentos …

Muito Obrigado,

Você deve ser capaz de falsificar isso usando uma célula personalizada para fazer suas linhas de header. Estes irão então rolar como qualquer outra célula na visão de tabela.

Você só precisa adicionar alguma lógica em seu cellForRowAtIndexPath para retornar o tipo de célula correto quando for uma linha de header.

Você provavelmente terá que gerenciar suas seções sozinho, ou seja, ter tudo em uma seção e falsificar os headers. (Você também pode tentar retornar uma visão oculta para a visualização do header, mas não sei se isso funcionará)

Uma maneira provavelmente mais fácil de conseguir isso:

Objetivo-C:

 CGFloat dummyViewHeight = 40; UIView *dummyView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.tableView.bounds.size.width, dummyViewHeight)]; self.tableView.tableHeaderView = dummyView; self.tableView.contentInset = UIEdgeInsetsMake(-dummyViewHeight, 0, 0, 0); 

Rápido:

 let dummyViewHeight = CGFloat(40) self.tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.tableView.bounds.size.width, height: dummyViewHeight)) self.tableView.contentInset = UIEdgeInsetsMake(-dummyViewHeight, 0, 0, 0) 

Os headers de seção agora rolam como qualquer célula comum.

Alterar o estilo da tabela de simples para agrupado (para quem já chegou aqui devido ao estilo de tabela errado)

 let tableView = UITableView(frame: .zero, style: .grouped) 

AVISO : esta solução implementa um método de API reservado. Isso pode impedir que o aplicativo seja aprovado pela Apple para distribuição na AppStore.

Eu descrevi os methods privados que transformam headers de seção flutuando no meu blog

Basicamente, você só precisa subclassificar o UITableView e retornar o NO em dois de seus methods:

 - (BOOL)allowsHeaderViewsToFloat; - (BOOL)allowsFooterViewsToFloat; 

Ok, eu sei que é tarde, mas eu tive que fazer isso. Já passei 10 horas pesquisando uma solução de trabalho, mas não encontrei uma resposta completa. Encontrei algumas dicas, mas difíceis para os iniciantes entenderem. Então eu tive que colocar meus 2 centavos e completar a resposta.

Como foi sugerido nas poucas respostas, a única solução de trabalho que eu pude implementar é inserir células normais na visão de tabela e tratá-las como headers de seção, mas a melhor maneira de obtê-las é inserindo essas células em linha 0 de cada seção. Dessa forma, podemos lidar com esses headers personalizados não flutuantes com muita facilidade.

Então, os passos são.

  1. Implemente o UITableView com o estilo UITableViewStylePlain.

     -(void) loadView { [super loadView]; UITableView *tblView =[[UITableView alloc] initWithFrame:CGRectMake(0, frame.origin.y, frame.size.width, frame.size.height-44-61-frame.origin.y) style:UITableViewStylePlain]; tblView.delegate=self; tblView.dataSource=self; tblView.tag=2; tblView.backgroundColor=[UIColor clearColor]; tblView.separatorStyle = UITableViewCellSeparatorStyleNone; } 
  2. Implemente titleForHeaderInSection como de costume (você pode obter esse valor usando sua própria lógica, mas eu prefiro usar delegates padrão).

     - (NSString *)tableView: (UITableView *)tableView titleForHeaderInSection:(NSInteger)section { NSString *headerTitle = [sectionArray objectAtIndex:section]; return headerTitle; } 
  3. Implemente numberOfSectionsInTableView como de costume

     - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { int sectionCount = [sectionArray count]; return sectionCount; } 
  4. Implemente numberOfRowsInSection como de costume.

     - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { int rowCount = [[cellArray objectAtIndex:section] count]; return rowCount +1; //+1 for the extra row which we will fake for the Section Header } 
  5. Retornar 0,0f em heightForHeaderInSection.

     - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { return 0.0f; } 
  6. NÃO implemente viewForHeaderInSection. Remova o método completamente em vez de retornar nulo.

  7. Em heightForRowAtIndexPath. Verifique se (indexpath.row == 0) e retorne a altura da célula desejada para o header da seção, caso contrário, retorne a altura da célula.

     - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if(indexPath.row == 0) { return 80; //Height for the section header } else { return 70; //Height for the normal cell } } 
  8. Agora em cellForRowAtIndexPath, verifique se (indexpath.row == 0) e implemente a célula como deseja que o header da seção seja e defina o estilo de seleção como none. ELSE implementa a célula como você quer que a célula normal seja.

     - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.row == 0) { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SectionCell"]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"SectionCell"] autorelease]; cell.selectionStyle = UITableViewCellSelectionStyleNone; //So that the section header does not appear selected cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SectionHeaderBackground"]]; } cell.textLabel.text = [tableView.dataSource tableView:tableView titleForHeaderInSection:indexPath.section]; return cell; } else { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Cell"] autorelease]; cell.selectionStyle = UITableViewCellSelectionStyleGray; //So that the normal cell looks selected cell.backgroundView =[[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"CellBackground"]]autorelease]; cell.selectedBackgroundView=[[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SelectedCellBackground"]] autorelease]; } cell.textLabel.text = [[cellArray objectAtIndex:indexPath.section] objectAtIndex:indexPath.row -1]; //row -1 to compensate for the extra header row return cell; } } 
  9. Agora, implemente willSelectRowAtIndexPath e retorne nil se indexpath.row == 0. Isso importará que didSelectRowAtIndexPath nunca seja acionado para a linha de header Section.

     - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.row == 0) { return nil; } return indexPath; } 
  10. E finalmente em didSelectRowAtIndexPath, verifique se (indexpath.row! = 0) e prossiga.

     - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.row != 0) { int row = indexPath.row -1; //Now use 'row' in place of indexPath.row //Do what ever you want the selection to perform } } 

Com isso você está feito. Agora você tem um header de seção não flutuante e com rolagem perfeita.

Em seu Interface Builder, clique no seu problema Table View

visão da tabela de problemas

Em seguida, navegue até o Attributes Inspector e altere Style Plain para Grouped;) Easy

fácil

O interessante sobre o UITableViewStyleGrouped é que o tableView adiciona o estilo às células e não ao TableView.

O estilo é adicionado como backgroundView às células como uma class chamada UIGroupTableViewCellBackground que manipula o desenho de segundo plano diferente de acordo com a posição da célula na seção.

Portanto, uma solução muito simples será usar UITableViewStyleGrouped, definir o backgroundColor da tabela como clearColor e simplesmente replace o backgroundView da célula em cellForRow:

 cell.backgroundView = [[[UIView alloc] initWithFrame:cell.bounds] autorelease]; 

Mude o seu estilo TableView:

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

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

UITableViewStylePlain – Uma visualização de tabela simples. Quaisquer headers ou rodapés de seção são exibidos como separadores inline e flutuam quando a exibição de tabela é rolada.

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

Espero que esta pequena mudança irá ajudá-lo ..

Existe outro caminho complicado. A idéia principal é dobrar o número da seção, e o primeiro mostra apenas o headerView, enquanto o segundo mostra as células reais.

 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return sectionCount * 2; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (section%2 == 0) { return 0; } return _rowCount; } 

O que você precisa fazer é implementar os delegates headerInSection:

 - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { if (section%2 == 0) { //return headerview; } return nil; } - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { if (section%2 == 0) { //return headerheight; } return 0; } 

Essa abordagem também tem pouco impacto em suas origens de dados:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { int real_section = (int)indexPath.section / 2; //your code } 

Comparando com outras abordagens, desta forma é seguro, sem alterar o quadro ou contentInsets da tableview. Espero que isso possa ajudar.

A maneira mais fácil de obter o que você deseja é definir seu estilo de tabela como UITableViewStyleGrouped , estilo separador como UITableViewCellSeparatorStyleNone :

 - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { return CGFLOAT_MIN; // return 0.01f; would work same } - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { return [[UIView alloc] initWithFrame:CGRectZero]; } 

Não tente exibir o rodapé de retorno como nil , não se esqueça de configurar a altura do header e a visualização do header, depois de obter o que deseja.

Embora isso possa não resolver o seu problema, ele fez para mim quando eu queria fazer uma coisa semelhante. Em vez de definir o header, usei o rodapé da seção acima. O que me salvou foi que essa seção era pequena e de natureza estática, por isso nunca rolava abaixo da parte inferior da exibição.

Enquanto pensava em como abordar esse problema, lembrei-me de um detalhe muito importante sobre o UITableViewStyleGrouped. A forma como o UITableView implementa o estilo agrupado (as bordas arredondadas ao redor das células) é adicionando um backgroundView personalizado ao UITableViewCells, e não ao UITableView. Cada célula é adicionada a um backgroundView de acordo com sua posição na seção (as linhas superiores obtêm a parte superior da borda da seção, as do meio obtêm a borda lateral e a parte inferior fica – bem, a parte inferior). Portanto, se quisermos apenas um estilo simples e não tivermos um backgroundView personalizado para nossas células (que é o caso em 90% das vezes), tudo o que precisamos fazer é usar UITableViewStyleGrouped e remover o plano de fundo personalizado . Isso pode ser feito seguindo estas duas etapas:

Alterar nosso estilo tableView para UITableViewStyleGrouped Adicione a seguinte linha ao cellForRow, antes de retornar a célula:

cell.backgroundView = [[[alocação de UIView] initWithFrame: cell.bounds] autorelease];

E é isso. O estilo tableView se tornará exatamente como UITableViewStylePlain, exceto pelos headers flutuantes.

Espero que isto ajude!

Para o Swift 3+

Simplesmente use UITableViewStyleGrouped e defina a altura do rodapé para zero com o seguinte:

 override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { return .leastNormalMagnitude } 

Outra maneira de fazer isso é criar uma seção vazia antes da que você quer que o header seja colocado e colocar seu header nessa seção. Como a seção está vazia, o header rolará imediatamente.

Você pode adicionar uma seção (com zero linhas) acima e, em seguida, definir a sectionFooterView acima como headerView da seção atual, footerView não flutua. Espero que dê uma ajuda.

Talvez você possa usar scrollViewDidScroll no tableView e alterar o contentInset base no header visível atual.

Parece funcionar para o meu caso de uso!

 extension ViewController : UIScrollViewDelegate{ func scrollViewDidScroll(_ scrollView: UIScrollView) { guard let tableView = scrollView as? UITableView, let visible = tableView.indexPathsForVisibleRows, let first = visible.first else { return } let headerHeight = tableView.rectForHeader(inSection: first.section).size.height let offset = max(min(0, -tableView.contentOffset.y), -headerHeight) self.tableView.contentInset = UIEdgeInsetsMake(offset, 0, -offset, 0) } } 

Ignore o XAK. Não explore nenhum método privado se você quiser que seu aplicativo tenha a chance de ser aceito pela Apple.

Isso é mais fácil se você estiver usando o Interface Builder. Você adicionaria um UIView no topo da view (onde as imagens aparecerão), então adicione sua tableview abaixo disso. IB deve dimensionar de acordo; ou seja, a parte superior da tableview toca a parte inferior da UIView que você acabou de adicionar e sua altura cobre o restante da canvas.

O pensamento aqui é que, se esse UIView não fizer parte da visão de tabela, ele não rolará com a tableview. isto é, ignore o header tableview.

Se você não está usando o construtor de interfaces, é um pouco mais complicado, porque você precisa obter o posicionamento e a altura corretos para a tableview.

Isso pode ser feito atribuindo manualmente a exibição de header no método viewDidLoad do UITableViewController, em vez de usar viewForHeaderInSection e heightForHeaderInSection do delegado. Por exemplo, em sua subclass de UITableViewController, você pode fazer algo parecido com isto:

 - (void)viewDidLoad { [super viewDidLoad]; UILabel *headerView = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 0, 40)]; [headerView setBackgroundColor:[UIColor magentaColor]]; [headerView setTextAlignment:NSTextAlignmentCenter]; [headerView setText:@"Hello World"]; [[self tableView] setTableHeaderView:headerView]; } 

A visualização do header desaparecerá quando o usuário rolar. Eu não sei porque isso funciona assim, mas parece conseguir o que você está procurando fazer.

Verifique a resposta sobre como implementar headers com o StoryBoard: exibições de header de tabela nos StoryBoards

Observe também que, se você não implementar

 viewForHeaderInSection:(NSInteger)section 

não flutua, o que é exatamente o que você quer.

Para remover completamente as seções do header da seção flutuante, você pode fazer isso:

 - (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { return [[UIView alloc] init]; } 

return nil não funciona.

Para desabilitar flutuante, mas ainda mostrar headers de seção, você pode fornecer uma visualização personalizada com seus próprios comportamentos.

Talvez você possa simplesmente tornar o fundo da exibição do header transparente:

 - (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section { view.tintColor = [UIColor clearColor]; } 

Ou aplique-o globalmente:

  [UITableViewHeaderFooterView appearance].tintColor = [UIColor clearColor]; 

De acordo com a resposta do @ samvermette, eu implementei o código acima no Swift para facilitar o uso rápido dos codificadores.

 let dummyViewHeight = CGFloat(40) self.tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.tableView.bounds.size.width, height: dummyViewHeight)) self.tableView.contentInset = UIEdgeInsetsMake(-dummyViewHeight, 0, 0, 0) 

ref: https://stackoverflow.com/a/26306212

let tableView = UITableView(frame: .zero, style: .grouped)

PLUS

 func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { if section == 0 { return 40 }else{ tableView.sectionHeaderHeight = 0 } return 0 } 

Isso ajudou a usar o espaço de header

Eu tenho outra solução ainda mais simples, para ser usada sem autolayout e com tudo feito através do XIB:

1 / Coloque o seu header na tableview arrastando-o e soltando-o diretamente na tableview.

2 / No Inspetor de Tamanho do header recém-criado, basta alterar seu dimensionamento automático: você deve deixar apenas as âncoras superior, esquerda e direita, além do preenchimento horizontal.

É assim que deve ser definido para o cabeçalho

Isso deve fazer o truque!

Você pode facilmente conseguir isso implementando o método viewForHeaderInSection na class de representante tableview. esse método espera um UIView como object de retorno (que é sua visualização de header). Eu fiz a mesma coisa no meu código