Cambiar el comportamiento de desplazamiento predeterminado del encabezado de sección UITableView

147

Tengo un UITableView con dos secciones. Es una vista de tabla simple. Estoy usando viewForHeaderInSection para crear vistas personalizadas para estos encabezados. Hasta aquí todo bien.

El comportamiento de desplazamiento predeterminado es que cuando se encuentra una sección, el encabezado de la sección permanece anclado debajo de la barra de navegación, hasta que la siguiente sección se desplaza a la vista.

Mi pregunta es esta: ¿puedo cambiar el comportamiento predeterminado para que los encabezados de sección NO permanezcan anclados en la parte superior, sino que se desplace debajo de la barra de navegación con el resto de las filas de sección?

¿Me estoy perdiendo algo obvio?

Gracias.

davidjhinson
fuente
1
¡Esa debe ser una de las mejores preguntas escritas en este sitio! :) Gracias.
Fattie
1
Verifique aquí la forma correcta de lograr este efecto stackoverflow.com/a/13735238/1880899
Sumit Anantwar el

Respuestas:

175

La forma en que resolví este problema es ajustar el de contentOffsetacuerdo con el contentInseten el UITableViewControllerDelegate(se extiende UIScrollViewDelegate) de esta manera:

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

El único problema aquí es que pierde un poco de rebote al desplazarse hacia arriba.


{NOTA: El "40" debe ser la altura exacta de SU encabezado de sección 0. Si usa un número que es más grande que la altura del encabezado de la sección 0, verá que la sensación del dedo se ve afectada (intente como "1000" y verá que el comportamiento de rebote es algo extraño en la parte superior). si el número coincide con la altura del encabezado de la sección 0, la sensación del dedo parece ser perfecta o casi perfecta.}

awulf
fuente
1
Solución perfecta que la anterior.
prathumca
44
¡Gracias! Esta es una solución perfecta. ¿Tiene la solución para el pie de página de la sección?
Tuan Nguyen
12
Esta solución no maneja correctamente la barra de menú de tap-on, que se supone que se desplaza al principio de la lista.
Reid Ellis
si necesita el mismo comportamiento para la vista de pie de página de sección, simplemente cambie la última línea de esta manera: scrollView.contentInset = UIEdgeInsetsMake (0, 0, sectionHeaderHeight, 0)
alex
Esto parece ser suficiente: scrollView.contentInset = UIEdgeInsetsMake (scrollView.contentOffset.y> = 0? -ScrollView.contentOffset.y: 0, 0, 0, 0);
MacMark
85

También puede agregar una sección con cero filas en la parte superior y simplemente usar el pie de página de la sección anterior como encabezado para la siguiente.

voidStern
fuente
13
En mi caso donde tengo más de 2 secciones, el pie de página se anclará en la parte inferior ..
Joseph Lin
3
Esto es creativo, pero debe ajustar indexPath si está utilizando un NSFetchedResultsController para cargar la tabla.
XJones
+1, con mucho, la MEJOR solución: ¡me llevó como 3 líneas implementarlas!
Jon
Brillante Brillante Brillante :-)
iphonic
¿Cómo funcionaría esto para el pie de página que ancla? Agradecería un poco de ayuda! Gracias
darwindeeds
36

Si hiciera esto, aprovecharía el hecho de que UITableViews en el estilo plano tiene los encabezados adhesivos y los del estilo agrupado no. Probablemente al menos intente usar una celda de tabla personalizada para imitar la apariencia de las celdas simples en una tabla agrupada.

En realidad no lo he intentado, así que puede que no funcione, pero eso es lo que sugeriría hacer.

Colin Barrett
fuente
Probablemente funcionaría, pero ¿se ha intentado? ¡Y parece mucho trabajo cuando la respuesta de @ voidStern funciona perfectamente!
bentford
La respuesta de voidStern no funciona para mí cuando tengo más de dos secciones. La respuesta de Colin es más simple / más elegante, sin la necesidad de cambiar manualmente el número de sección y agregar el recuento de sección.
Joseph Lin
Consejos: utilícelo tableView.separatorColor = [UIColor clearColor];para imitar una vista de tabla simple.
Joseph Lin
3
Intenté hacer esto, pero cuando no pude hacer que las celdas de la tabla agrupadas se parecieran a las simples, es decir, elimine el margen izquierdo y derecho a ambos lados de las celdas. ¿Como hiciste esto?
Adam Carter
1
Muchas gracias por aclarar las cosas sobre UITableViewStyle, es decir, agrupadas y simples, que deciden si el encabezado / pie de página de la vista de tabla será fijo o no. Muchas gracias @Colin Barrett
Dhaval H. Nena
28

Sé que llega tarde, ¡pero he encontrado la solución definitiva!

Lo que quiere hacer es si tiene 10 secciones, deje que dataSource devuelva 20. Use números pares para los encabezados de sección y números impares para el contenido de la sección. algo como esto

- (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á! :RE

LocoMike
fuente
gran idea. pero si tienes títulos de índice de sección, sospecho que esto los arruinará.
roocell
¿por qué? Puedes hacer lo mismo con los títulos de las secciones. Solo recuerde nulo para números impares y un título para el número par.
LocoMike
1
Sí, este método funciona muy bien. Fue una gran cantidad de contabilidad (probablemente relacionada con la forma en que tengo que obtener los títulos de las secciones) Pero funcionó. El truco es que numberOfRowsInSection y cellForRowAtIndexPath usan if (section% 2! = 0 && section! = 0) mientras que titleForHeaderInSection y las otras funciones de índice de sección usan if (section% 2 == 0)
roocell
el autor debe elegir esto como la respuesta. Sería genial si hubiera una forma de subclasificar uitableview para anclar los títulos de las secciones automáticamente.
roocell
Esto funcionó mejor para mí. Primero probé la solución de @ Colin, pero no funcionó bien con la muy personalizada UITableViewque estoy usando.
kubi
15

Hay varias cosas que deben hacerse para resolver este problema de una manera no hacky:

  1. Establezca el estilo de vista de tabla en UITableViewStyleGrouped
  2. Establezca la vista de tabla backgroundColoren[UIColor clearColor]
  3. Establezca la backgroundViewcelda de vista de cada tabla en una vista vacía conbackgroundColor [UIColor clearColor]
  4. Si es necesario, configure la vista de tabla de manera rowHeightadecuada o anule tableView:heightForRowAtIndexPath:si las filas individuales tienen alturas diferentes.
Neil Gall
fuente
(+1) @Neil, probé tu respuesta pero desafortunadamente, UITableViewStyleGroupedlas celdas de la tabla tienen márgenes adicionales que cambian su alineación con el encabezado. Este es un problema cuando realmente desea representar una tabla de varias columnas y usar el encabezado para mostrar los títulos de las columnas (y luego hacer que las entradas individuales de la tabla se alineen con estos títulos).
brainjam
15

Publicado originalmente aquí , una solución rápida utilizando el IB. Lo mismo se puede hacer mediante programación aunque de manera bastante simple.

Una forma probablemente más fácil de lograr esto (usando IB):

Arrastre una UIView a su TableView para convertirla en su vista de encabezado.

  1. Establezca esa altura de vista de encabezado en 100px
  2. Establezca el contenido de la vista de tabla (superior) en -100
  3. Los encabezados de sección ahora se desplazarán como cualquier celda normal.

Algunas personas comentaron que esta solución oculta el primer encabezado, sin embargo, no he notado ningún problema. Funcionó perfectamente para mí y fue, con mucho, la solución más simple que he visto hasta ahora.

Doc
fuente
Tenga en cuenta que probablemente debería configurar la vista para usar un color de fondo de color claro, de lo contrario, si se desplaza más allá de la parte superior, verá blanco en el rebote.
Doc
3
Esconde mi primer encabezado
Leena
Leena, ¿en qué versión de iOS estás ejecutando? ¿Es la vista un UITableViewController o una UIView con un UITableView en él? No estoy seguro de por qué para algunas personas esta solución oculta el primer encabezado, así que estoy tratando de encontrar alguna discrepancia.
Doc.
Esta solución es perfecta. Estoy buscando una apariencia de fondo de celda superior e inferior infinita, esto lo logra totalmente. ¡Agradable!
Taylor Halliday
Esta solución es excelente. Más fácil de lograr que las otras respuestas en este hilo. En mi opinión, esta debería ser la respuesta aceptada.
John Rogers
15

No estaba contento con las soluciones descritas aquí hasta ahora, así que intenté combinarlas. El resultado es el siguiente código, inspirado en @awulf y @cescofry. Funciona para mí porque no tengo un encabezado de vista de tabla real. Si ya tiene un encabezado de vista de tabla, puede que tenga que ajustar la 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
fuente
Esta fue una solución más simple que encontré.
Zorayr
Esto funcionó para mí en iOS9.2, por lo que, aunque es una respuesta anterior, sigue siendo válida. Y no ocultó el encabezado de la primera sección. Funciona y funciona perfectamente.
idStar
Perfecto. Si su vista de tabla ya tiene un encabezado, solo aumente su altura en el desplazamiento y luego aumente la constante de restricción superior del contenido del encabezado para desplazar el contenido a la posición original
Jason
14

Simplemente cambie el estilo de TableView:

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

Documentación UITableViewStyle :

UITableViewStylePlain- Una vista de tabla simple. Cualquier encabezado o pie de página se muestra como separador en línea y flotante 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 no flotan.

Arthur S
fuente
Gran respuesta, funcionó para mí. no hay necesidad de trabajar
Shams Ahmed
9

Seleccionar estilo de vista de tabla agrupada

Seleccione el estilo de Vista de tabla agrupada del Inspector de atributos de tableView en el guión gráfico.

Arjun Shukla
fuente
5

Establezca el headerView de la tabla con una vista transparente con la misma altura del encabezado en la vista en sección. También inicie la vista de tabla con un marco a 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
fuente
4

Encontré una solución alternativa, use la primera celda de cada sección en lugar de una sección de encabezado real, esta solución no parece tan limpia, pero funciona tan bien, puede usar una celda prototipo definida para su sección de encabezados y en el método cellForRowAtIndexPath solicite indexPath.row == 0, si es verdadero, use la celda prototipo de la sección de encabezado, de lo contrario use su celda prototipo predeterminada.

AlexBB
fuente
Si aún desea trabajar con indexPath.row& indexPath.section, asegúrese de devolver una altura de 0.0fpara - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)sectionque sus encabezados sean invisibles.
Sopa
4

Cambie su estilo de TableView:

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

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

UITableViewStylePlain- Una vista de tabla simple. Cualquier encabezado o pie de página se muestra como separador en línea y flotante 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 no flotan. Espero que este pequeño cambio te ayude.

Aks
fuente
2

Ahora que el estilo agrupado se ve básicamente igual que el estilo plano en iOS 7 (en términos de planitud y fondo), para nosotros la mejor y más fácil (es decir, menos hacky) fue simplemente cambiar el estilo de la vista de tabla a agrupado. Jacking con contentInsets siempre fue un problema cuando integramos una barra de navegación desplazable en la parte superior. Con un estilo de vista de tabla agrupada, se ve exactamente igual (con nuestras celdas) y los encabezados de sección permanecen fijos. Sin desplazamiento de rareza.

Greg Combs
fuente
Esta es la mejor solución, especialmente si las celdas son personalizadas, parece que simplemente detiene la "retención" del encabezado de sección cuando el desplazamiento toca la parte superior.
Acero reciclado
1

Asigne un recuadro negativo a su tableView. Si tiene encabezados de sección alta de 22px, y no desea que sean adhesivos, justo después de volver a cargar los datos, agregue:

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

Funciona como un encanto para mí. También funciona para los pies de página de sección, solo asigne el recuadro negativo en la parte inferior.

Mike A
fuente
¿Esto simplemente corta el primer encabezado?
cannyboy 01 de
¡Me funcionó perfectamente! Gracias
Daniel
0

Agrego la tabla a una Vista de desplazamiento y eso parece funcionar bien.

Dolbex
fuente
0

Mira mi respuesta aquí . Esta es la forma más fácil de implementar los encabezados de sección no flotantes sin ningún truco.

Sumit Anantwar
fuente
0

La respuesta de @ LocoMike se ajustó mejor a mi tableView, sin embargo, también se rompió al usar pies de página. Entonces, esta es la solución corregida cuando se usan encabezados y pies de página:

- (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
fuente
0

Versión rápida de la respuesta @awulf, ¡que funciona muy bien!

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
fuente
-2

Aprendí que solo establecer la propiedad tableHeaderView lo hace, es decir:

 tableView.tableHeaderView = customView;

y eso es.

Soni
fuente