2009-09-18 4 views
22

Mi aplicación necesita tener celdas de tabla de altura variable (ya que en cada celda de tabla difiere en altura, no es necesario que cada celda pueda redimensionarse).¿Cómo puedo hacer celdas de tabla de altura variable en el iPhone correctamente?

Tengo una solución que actualmente funciona, pero es kludgy y lenta.

Mi solución actual:

para poder efectuar las celdas de la tabla, calculo que tan alto cada célula tiene que ser mediante llamadas a métodos de dimensionamiento como -sizeWithFont:constrainedToSize: en sus datos. Luego sumar las alturas, permitir algo de relleno y almacenar el resultado con los datos.

Luego, cuando mi UITableViewDelegate recibe el -tableview:heightForRowAtIndexPath:, averiguo qué elemento se representará para esa celda y devolveré la altura que calculé anteriormente.

Como dije, esto funciona, pero llamar a -sizeWithFont:constrainedToSize: es muy lento cuando lo haces para cientos de elementos secuencialmente, y creo que se puede hacer mejor.

Para que esto funcione, tenía que mantener dos partes de código, una que calcule las alturas de las celdas, y una que realmente dibuje las celdas cuando llegue el momento.

Si algo sobre el elemento de modelo cambió, tuve que actualizar estos dos trozos de código, y de vez en cuando todavía no coinciden perfectamente, a veces resultando en celdas de tabla que son un poco demasiado pequeñas para un determinado artículo, o demasiado grande.

Mi Solución propuesta:

por lo que quiero acabar con el calcular previamente la altura de la celda. A) porque rompe el paradigma MVC y B) porque es lento.

Así que mi celda se dibuja a sí misma, y ​​como resultado, termina con la altura de celda correcta. Mi problema es que no tengo forma de decirle a la mesa que vea la altura de la celda antes de que se dibuje, y para entonces ya es demasiado tarde.

Intenté llamar a -cellForRowAtIndexPath: desde -tableView:heightForRowAtIndexPath: pero esto se atasca en un bucle infinito, ya que el primero llama al segundo en algún punto, y viceversa (al menos esto es lo que vi cuando lo probé).

Así que esa opción está fuera de cuestión.

Si no especifico un tamaño en la altura para el método de delegado de fila, entonces la vista de tabla se vuelve gruesa. Las celdas tienen la altura perfecta, pero su posición x es la de las celdas de alturas fijas.

Messed Table Cells http://jamsoftonline.com/images/messed_table_cells.png

Aviso cómo la célula inferior es el tamaño correcto - es sólo la superposición de la celda anterior, y la celda anterior se superpone su anterior, y así sucesivamente y así sucesivamente.

También con este método, mientras se desplaza hay algunos artefactos que creo que pueden estar relacionados con el identificador de reutilización de las celdas.

Así que cualquier ayuda aquí sería gratamente apreciada.

+0

tengo el mismo problema. -cellForRowAtIndexPath: calls -tableView: heightForRowAtIndexPath: si se llama en este último (observe que no llama a este último si no se llama en este último) me vuelve loco. – an0

Respuesta

38

Esto es lo que uso. NSString tiene un método que le indicará las dimensiones de un cuadro de texto en función de la información de la fuente y las restricciones de altura/ancho que le dé.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    NSString *text = [self getTextForIndexPath:indexPath]; 
    UIFont *font = [UIFont systemFontOfSize:14]; 
    CGSize size = [self getSizeOfText:text withFont:font]; 

    return (size.height + 11); // I put some padding on it. 
} 

A continuación, se escribe un método de tirar del texto para esta celda ...

- (NSString *)getTextForIndexPath:(NSIndexPath *)indexPath 
{ 
    NSString *sectionHeader = [self.tableSections objectAtIndex:[indexPath section]]; 
    NSString *sectionContent = [self.tableData objectForKey:sectionHeader]; 

    return sectionContent; 
} 

Y esto es para obtener el tamaño del texto.

- (CGSize)getSizeOfText:(NSString *)text withFont:(UIFont *)font 
{ 
    return [text sizeWithFont:font constrainedToSize:CGSizeMake(280, 500)]; 
} 
+0

¿Quién votó en contra y por qué? – Jasarien

+1

¿Cómo puedo usar este método solo para una celda en mi tabla agrupada Vista? Me gustaría devolver la altura de la celda en función de la altura de una vista de texto dentro de la celda ... – matteodv

+0

Hola. Quiero saber qué es 'self.tablesections' y' self.tableData'. Sé que son NSArray y NSDictionary, pero ¿esto significa que creó una matriz de secciones de tabla y un diccionario para el contenido de estas secciones? Pregunto porque solo uso una sección y parece que no puedo hacer que funcione. –

2

Es sólo una idea:

  • ¿Y si tuviera, digamos, seis tipos diferentes de células, cada una con su propio identificador y una altura fija. Uno sería para una celda de una línea, el otro para una celda de dos líneas, etc.

  • Cada vez que cambie su modelo, calcule la altura de esa fila y luego busque el tipo de celda más cercano que tenga la altura más cercana a lo que necesitas Guarde ese identificador de tipo de celda con el modelo. También puede almacenar la altura de fila fija para esa celda en el modelo para que pueda devolverla en la vista de tabla: heightForRowAtIndexPath (no me cansaría de forzarlo a calcular dentro de la clase de celda; técnicamente no es parte de la funcionalidad de dibujo de la celda y más algo que usa la tabla para decidir qué tipo de celda crear).

  • En tiempo de ejecución, cuando se le pide que devuelva una celda para esa fila, todo lo que necesita hacer es crear (u obtener de la memoria caché de celda) una celda con el identificador de tipo de celda, cargar los valores y listo.

Si el cálculo altura de la celda es demasiado lento, entonces usted podría tirar el mismo truco de la caché tableview hace y lo hacen sólo en la demanda cuando la célula entra en la visión. En un momento dado, solo tendrías que hacerlo para las celdas a la vista, y luego solo para una sola celda mientras se desplaza a la vista en cada extremo.

+0

He jugado con esta idea en el pasado y no pude lograr que hiciera exactamente lo que quería. Pero tal vez puedo intentarlo de nuevo. – Jasarien

+0

Una buena y divertida forma de hacerlo mal –

0

que darse cuenta de que esto no funcionará para usted debido al bucle infinito que mencionas, pero he tenido cierto éxito con llamar a las células layoutSubViews método

Aunque esto puede ser un poco ineficaz debido a llamadas múltiples a cellForRowAtIndexPath y layoutSubViews, encuentro que el código es más limpio.

-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    MyCell *cell = (MyCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath]; 

    [cell layoutSubviews]; 

    return CGRectGetHeight(cell.frame); 
} 

Y en el código de diseño:

- (void)layoutSubviews 
{ 
    [super layoutSubviews]; 

    //First expand the label to a large height to so sizeToFit isn't constrained 
    [self.myArbitrarilyLengthLabel setFrame:CGRectMake(self.myArbitrarilyLengthLabel.frame.origin.x, 
              self.myArbitrarilyLengthLabel.frame.origin.y, 
              self.myArbitrarilyLengthLabel.frame.size.width, 
              1000)]; 


    //let sizeToFit do its magic 
    [self.myArbitrarilyLengthLabel sizeToFit]; 

    //resize the cell to encompass the newly expanded label 
    [self setFrame:CGRectMake(self.frame.origin.x, 
          self.frame.origin.y, 
          self.frame.size.width, 
           CGRectGetMaxY(self.myArbitrarilyLengthLabel.frame) + 10)]; 
} 
Cuestiones relacionadas