¿Es posible deshabilitar los encabezados flotantes en UITableView con UITableViewStylePlain?

Estoy usando una UITableView para diseñar ‘páginas’ de contenido. Estoy usando los encabezados de la vista de tabla para diseñar ciertas imágenes, etc. y preferiría que no flotasen pero permanecieran estáticas como lo hacen cuando el estilo está configurado en UITableViewStyleGrouped .

Además de usar UITableViewStyleGrouped , ¿hay alguna manera de hacerlo? Me gustaría evitar el uso agrupado ya que agrega un margen en todas mis celdas y requiere deshabilitar la vista de fondo para cada una de las celdas. Me gustaría el control total de mi diseño. Idealmente serían un “UITableViewStyleBareBones”, pero no vi esa opción en los documentos …

Muchas gracias,

Debería poder falsificar esto mediante el uso de una celda personalizada para hacer sus filas de encabezado. Estos se desplazarán como cualquier otra celda en la vista de tabla.

Solo necesita agregar algo de lógica en su cellForRowAtIndexPath para devolver el tipo de celda correcto cuando se trata de una fila de encabezado.

Sin embargo, probablemente tendrá que administrar las secciones usted mismo, es decir, tener todo en una sección y falsificar los encabezados. (También podría intentar devolver una vista oculta para la vista de encabezado, pero no sé si eso funcionará)

Una forma probablemente más fácil de lograr esto:

C objective:

 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) 

Los encabezados de sección ahora se desplazarán como cualquier celda normal.

Cambiar el estilo de la tabla de simple a agrupado (Para quién llegó aquí debido al estilo incorrecto de la mesa)

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

ADVERTENCIA : esta solución implementa un método API reservado. Esto podría evitar que la aplicación sea aprobada por Apple para su distribución en la AppStore.

Describí los métodos privados que gira en los encabezados de las secciones que flotan en mi blog

Básicamente, solo necesita subclasificar UITableView y devolver NO en dos de sus métodos:

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

Ok, sé que es tarde pero tuve que hacerlo. Pasé 10 horas buscando una solución de trabajo pero no encontré una respuesta completa. Encontré algunos consejos pero es difícil de entender para los principiantes. Así que tuve que poner mis 2 centavos y completar la respuesta.

Como se ha sugerido en algunas de las respuestas, la única solución de trabajo que pude implementar es insertar celdas normales en la vista de tabla y manejarlas como encabezados de sección, pero la mejor manera de lograrlo es insertando estas celdas en fila 0 de cada sección. De esta forma podemos manejar estos encabezados no flotantes personalizados muy fácilmente.

Entonces, los pasos son

  1. Implementar UITableView con el 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 siempre (puede obtener este valor usando su propia lógica, pero prefiero usar delegates estándar).

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

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

     - (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. Devuelve 0.0f en heightForHeaderInSection.

     - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { return 0.0f; } 
  6. NO implemente viewForHeaderInSection. Elimine el método completamente en lugar de devolver nil.

  7. En heightForRowAtIndexPath. Compruebe si (indexpath.row == 0) y devuelva la altura de celda deseada para el encabezado de sección, de lo contrario, devuelva la altura de la celda.

     - (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. Ahora en cellForRowAtIndexPath, compruebe si (indexpath.row == 0) e implemente la celda como desee que sea el encabezado de la sección y establezca el estilo de selección en none. ELSE implementa la celda como desea que sea la celda normal.

     - (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. Ahora implemente willSelectRowAtIndexPath y devuelva nil si indexpath.row == 0. Esto le importará que didSelectRowAtIndexPath nunca se active para la fila de encabezado de sección.

     - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.row == 0) { return nil; } return indexPath; } 
  10. Y finalmente en didSelectRowAtIndexPath, verifique si (indexpath.row! = 0) y proceda.

     - (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 } } 

Con esto has terminado. Ahora tiene un encabezado de sección perfectamente desplegable y no flotante.

En su Interface Builder haga clic en su problema Table View

vista de tabla de problemas

A continuación, navegue hasta Attributes Inspector y cambie Style Plain to Grouped;) Fácil

fácil

Lo interesante de UITableViewStyleGrouped es que tableView agrega el estilo a las celdas y no a TableView.

El estilo se agrega como vista de fondo a las celdas como una clase llamada UIGroupTableViewCellBackground que maneja el dibujo de fondo diferente según la posición de la celda en la sección.

Así que una solución muy simple será usar UITableViewStyleGrouped, establecer el backgroundColor de la tabla en clearColor, y simplemente reemplazar la vista de fondo de la celda en cellForRow:

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

Cambie su estilo TableView:

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

Según la documentación de apple para UITableView:

UITableViewStylePlain- Una vista de tabla simple. Los encabezados o pies de página de cada sección se muestran como separadores en línea y flotan cuando se desplaza la vista de tabla.

UITableViewStyleGrouped: una vista de tabla cuyas secciones presentan distintos grupos de filas. Los encabezados y pies de página de la sección no flotan.

Espero que este pequeño cambio te ayude …

Hay otra manera complicada. La idea principal es duplicar el número de sección, y el primero solo muestra el headerView, mientras que el segundo muestra las celdas reales.

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

Lo que hay que hacer entonces es implementar los delegates de 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; } 

Este enfoque también tiene poco impacto en sus fonts de datos:

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

Comparando con otros enfoques, de esta manera es seguro sin cambiar el marco o contentInsets de la tabla vista. Espero que esto pueda ayudar.

La forma más sencilla de obtener lo que desea es establecer el estilo de su tabla 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]; } 

No intente devolver la vista de pie de página como nil , no olvide establecer la altura del encabezado y la vista de encabezado, después de que debe obtener lo que desea.

Aunque esto puede no resolver su problema, lo hizo por mí cuando quería hacer algo similar. En lugar de establecer el encabezado, utilicé el pie de página de la sección anterior. Lo que me salvó fue que esta sección era pequeña y de naturaleza estática, por lo que nunca se desplazó por debajo de la parte inferior de la vista.

Mientras pensaba cómo abordar este problema, recordé un detalle muy importante sobre UITableViewStyleGrouped. La forma en que UITableView implementa el estilo agrupado (los bordes redondeados alrededor de las celdas) es agregando una vista de fondo personalizada a UITableViewCells, y no a UITableView. A cada celda se agrega una vista de fondo según su posición en la sección (las filas superiores obtienen la parte superior del borde de la sección, las del medio obtienen el borde lateral y la inferior, bueno, la parte inferior). Entonces, si solo queremos un estilo simple, y no tenemos una vista de fondo personalizada para nuestras celdas (que es el caso en el 90% de las veces), entonces todo lo que tenemos que hacer es usar UITableViewStyleGrouped, y eliminar el fondo personalizado . Esto se puede hacer siguiendo estos dos pasos:

Cambie nuestro estilo tableView a UITableViewStyleGrouped Agregue la siguiente línea a cellForRow, justo antes de devolver la celda:

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

Y eso es. El estilo tableView se convertirá exactamente como UITableViewStylePlain, a excepción de los encabezados flotantes.

¡Espero que esto ayude!

Para Swift 3+

Simplemente use UITableViewStyleGrouped y establezca la altura del pie de página en cero con lo siguiente:

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

Otra forma de hacerlo es crear una sección vacía justo antes de la que desea que el encabezado y poner su encabezado en esa sección. Como la sección está vacía, el encabezado se desplazará de inmediato.

Puede agregar una sección (con cero filas) arriba, luego configure la sección anteriorFooterView como el headerView de la sección actual, footerView no flota. Espero que te ayude.

Tal vez podría usar scrollViewDidScroll en tableView y cambiar el contentInset basado en el encabezado visible actual.

¡Parece que funciona para mi 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) } } 

Ignora XAK. No explore ningún método privado si desea que su aplicación tenga la posibilidad de ser aceptada por Apple.

Esto es más fácil si usa Interface Builder. Agregaría una UIView en la parte superior de la vista (donde irán las imágenes), luego agregue su tabla debajo. IB debe medirlo según corresponda; es decir, la parte superior de la vista de tabla toca la parte inferior de la UIView que acaba de agregar y su altura cubre el rest de la pantalla.

El pensamiento aquí es que si esa UIView no es realmente parte de la vista de tabla, no se desplazará con la vista de tabla. es decir, ignorar el encabezado de la vista de tabla.

Si no está utilizando el constructor de interfaces, es un poco más complicado porque debe obtener el posicionamiento y la altura correctos para la tabla vista.

Esto se puede lograr asignando manualmente la vista de encabezado en el método viewDidLoad de UITableViewController en lugar de utilizar viewForHeaderInSection y heightForHeaderInSection del delegado. Por ejemplo, en su subclase de UITableViewController, puede hacer algo como esto:

 - (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]; } 

La vista del encabezado desaparecerá cuando el usuario se desplace. No sé por qué esto funciona así, pero parece lograr lo que estás buscando hacer.

Consulte la respuesta sobre cómo implementar encabezados con StoryBoard: Vistas de encabezado de tabla en StoryBoards

También tenga en cuenta que si no implementa

 viewForHeaderInSection:(NSInteger)section 

no flotará, que es exactamente lo que quieres.

Para eliminar por completo las secciones del encabezado de la sección flotante, puede hacer esto:

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

return nil no funciona.

Para deshabilitar flotante pero aún mostrar encabezados de sección, puede proporcionar una vista personalizada con sus propios comportamientos.

Tal vez simplemente puede hacer que el fondo de la vista de encabezado sea transparente:

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

O aplicarlo globalmente:

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

De acuerdo con la respuesta de @ samvermette, implementé el código anterior en Swift para que los codificadores puedan usarlo rápidamente.

 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)

PLUSE

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

Esto ayudó a usar el espacio del encabezado

Tengo otra solución aún más simple, para ser utilizada sin autolayout y con todo lo hecho a través del XIB:

1 / Coloque su encabezado en la vista de tabla arrastrando y soltándolo directamente en la vista de tabla.

2 / En el Inspector de tamaño del encabezado recién creado, simplemente cambie su autoestación: solo debe dejar los anclajes superior, izquierdo y derecho, más el relleno horizontalmente.

Así es como debe establecerse para el encabezado

Eso debería hacer el truco !

Puede lograrlo fácilmente implementando el método viewForHeaderInSection en la clase de delegado de tableview. este método espera un UIView como objeto de retorno (que es su vista de encabezado). He hecho lo mismo en mi código