La configuración de altura de fila de celda personalizada en el guión gráfico no responde

213

Estoy tratando de ajustar la altura de la celda para una de las celdas en mi vista de tabla. Estoy ajustando el tamaño de la configuración de "altura de fila" dentro del "inspector de tamaño" de la celda en cuestión. Cuando ejecuto la aplicación en mi iPhone, la celda tiene el tamaño predeterminado establecido en el "tamaño de fila" en la vista de tabla.

Si cambio el "tamaño de fila" de la vista de tabla, el tamaño de todas las celdas cambia. No quiero hacer eso, ya que quiero un tamaño personalizado solo para una celda. He visto muchas publicaciones que tienen una solución programática al problema, pero preferiría hacerlo a través del guión gráfico, si es posible.

zirinisp
fuente

Respuestas:

295

En las celdas dinámicas , rowHeightestablecer en UITableView siempre anula la altura de fila de las celdas individuales.

Pero en celdas estáticas , rowHeightestablecer celdas individuales puede anular UITableView.

¿No está seguro si es un error, Apple podría estar haciendo esto intencionalmente?

pixelfreak
fuente
36
Respuesta correcta n. ° 3, y específicamente porque esta respuesta se aplica a Interface Builder / Storyboards. Si selecciona la celda en IB, el Inspector de tamaño muestra Altura de fila en la parte superior (con una casilla de verificación "personalizada"), pero si selecciona la vista de tabla completa , el Inspector de tamaño muestra Altura de fila en la parte superior también (no "personalizado" " en este caso). Como dice pixelfreak, solo se usa la configuración de vista de tabla para las celdas dinámicas. (No estoy seguro si es intencional)
Ruibarbo
8
esta respuesta implica que la solución sería simplemente cambiar el UITableViewcontenido de Dynamic Prototypesa Static Cells, lo hice, y todo mi proyecto explotó ... casi me mato.
Abbood
44
¿Hay alguna forma de recuperar este número? ¿La única forma de profundizar es sumergirse en el guión gráfico y sacarlo de allí?
Biclops
Definitivamente NO es un error, porque su tabla es dinámica, entonces, ¿cómo podría saber el sistema dónde ha ido a usar cada una de esas celdas? Y cuántas veces se usaría cada prototipo.
Vincent Bernier
También parece haber un problema si inicialmente configura una vista de tabla como "celdas estáticas" y luego la cambia a "prototipos dinámicos". Tuve un problema donde incluso el método delegado para rowHeight estaba siendo ignorado. Primero lo resolví editando directamente el XML del guión gráfico, luego finalmente reconstruí la escena del guión gráfico desde cero, utilizando prototipos dinámicos desde el principio.
Eric Goldberg
84

Si usa UITableViewController, implemente este método:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;

En la función de una fila, puede elegir Altura. Por ejemplo,

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row == 0) {
       return 100;
    } 
    else {
       return 60;
    }
}

En este ejemplo, la altura de la primera fila es de 100 píxeles y las otras son de 60 píxeles.

Espero que este te pueda ayudar.

Beber
fuente
2
Beber, ¿SIEMPRE uno tiene que hacer ambas cosas (marque Personalizado y edite el valor de Altura de fila en el guión gráfico y & especifíquelo en heightForRowAtIndexPath)?
marciokoko
¿Podrías ayudarme con respecto a esto? Necesito tener celdas dinámicas con altura dinámica, pero si uso este método, no importa lo que devuelva, todas las celdas desaparecerán. Excepto en el iPhone 5 el dispositivo , donde funciona como se esperaba. ¿Puedes entender por qué?
Ostmeistro
34

Para celdas dinámicas, rowHeightestablecer en UITableViewsiempre anula las celdas individuales ' rowHeight.

Este comportamiento es, en mi opinión, un error. Cada vez que tenga que administrar su interfaz de usuario en dos lugares, es propenso a errores. Por ejemplo, si cambia el tamaño de su celda en el guión gráfico, también debe recordar cambiarlos en el heightForRowAtIndexPath:. Hasta que Apple corrija el error, la mejor solución actual es anular heightForRowAtIndexPath:, pero usar las celdas prototipo reales del guión gráfico para determinar la altura en lugar de usar números mágicos . Aquí hay un ejemplo:

- (CGFloat)tableView:(UITableView *)tableView 
           heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    /* In this example, there is a different cell for
       the top, middle and bottom rows of the tableView.
       Each type of cell has a different height.
       self.model contains the data for the tableview 
    */
    static NSString *CellIdentifier;
    if (indexPath.row == 0) 
        CellIdentifier = @"CellTop";
    else if (indexPath.row + 1 == [self.model count] )
        CellIdentifier = @"CellBottom";
    else
        CellIdentifier = @"CellMiddle";

    UITableViewCell *cell = 
              [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    return cell.bounds.size.height;
}

Esto asegurará que cualquier cambio en la altura de las celdas de su prototipo se detecte automáticamente en tiempo de ejecución y solo necesita administrar su IU en un solo lugar: el guión gráfico.

memmons
fuente
2
He publicado una respuesta con el código completo, incluido el almacenamiento en caché; ver stackoverflow.com/a/16881312/292166
JosephH
1
¡Wow wow wow! ¡Voté por respuesta, pero ten CUIDADO! Este método causa no solo una penalización de rendimiento como dijo @Brennan en los comentarios, sino que también causa una asignación de memoria cada vez mayor en cada reloadData, ¡algo así como una pérdida de memoria! ¡Es necesario usar la solución de lensovet anterior! ¡Pase un día para detectar esta pérdida de memoria!
skywinder
No funciona en Swift, porque cell.bounds.size.height siempre devuelve 0.0
King-Wizard
1
Las celdas aún no existen en el momento en que el sistema llamó a su método tableView: heightForRowAtIndexPath. Se supone que puede usar el indexPath que se le pasa para indexar en su modelo de datos y ver qué tipo de celda se usará allí, y luego calcular la altura de esa celda y devolverla. Eso permite que el sistema coloque las celdas en la tabla antes de crear celdas.
King-Wizard
1
Además, no debe llamar a su propio método cellForRowAtIndexPath. Las vistas de tabla tienen un método cellForRowAtIndexPath que devolverá una celda para esa ruta de índice si actualmente está visible en la pantalla, pero a menudo esa ruta de índice no tiene una celda asociada.
King-Wizard
22

He creado el código que sugieren las diversas respuestas / comentarios para que funcione para guiones gráficos que usan celdas prototipo.

Este código:

  • No requiere que la altura de la celda se establezca en otro lugar que no sea el lugar obvio en el guión gráfico
  • Almacena en caché la altura por razones de rendimiento
  • Utiliza una función común para obtener el identificador de celda para una ruta de índice para evitar la lógica duplicada

Gracias a Answerbot, Brennan y Lensovet.

- (NSString *)cellIdentifierForIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = nil;

    switch (indexPath.section)
    {
        case 0:
            cellIdentifier = @"ArtworkCell";
            break;
         <... and so on ...>
    }

    return cellIdentifier;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = [self cellIdentifierForIndexPath:indexPath];
    static NSMutableDictionary *heightCache;
    if (!heightCache)
        heightCache = [[NSMutableDictionary alloc] init];
    NSNumber *cachedHeight = heightCache[cellIdentifier];
    if (cachedHeight)
        return cachedHeight.floatValue;

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    CGFloat height = cell.bounds.size.height;
    heightCache[cellIdentifier] = @(height);
    return height;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *cellIdentifier = [self cellIdentifierForIndexPath:indexPath];

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];

    <... configure cell as usual...>
JosephH
fuente
Sé que esta es una respuesta anterior, pero ¿alguien más encuentra que esto resulta en un desbordamiento de pila? Cuando se carga la vista, obtengo UISectionRowData refreshWithSection: tableViewRowData: que llama a tableView: heightForRowAtIndexPath: que llama a dequeueReusableCellWithIdentifier: que llama a [UISectionRowData ...] así que tengo un desbordamiento de pila recursivo. Realmente quería usar esta solución, pero no parece funcionar con iOS7.
sbaker
No funciona en Swift, porque cell.bounds.size.height siempre devuelve 0.0
King-Wizard
Las celdas aún no existen en el momento en que el sistema llamó a su método tableView: heightForRowAtIndexPath. Se supone que puede usar el indexPath que se le pasa para indexar en su modelo de datos y ver qué tipo de celda se usará allí, y luego calcular la altura de esa celda y devolverla. Eso permite que el sistema coloque las celdas en la tabla antes de crear celdas.
King-Wizard
Además, no debe llamar a su propio método cellForRowAtIndexPath. Las vistas de tabla tienen un método cellForRowAtIndexPath que devolverá una celda para esa ruta de índice si actualmente está visible en la pantalla, pero a menudo esa ruta de índice no tiene una celda asociada.
King-Wizard
@ snow-tiger No he intentado esto rápidamente, pero no entiendo tus comentarios. No 'llamo [mi] propio método cellForRowAtIndexPath', y es deliberado que no lo haga. También soy consciente de que las celdas no existen en el momento en que se llama a tableView: heightForRowAtIndexPath, ese es el objetivo de este código y por qué usa dequeueReusableCellWithIdentifier para obtener una celda. El código que se publica en esta respuesta funciona. O bien, hay algo más que sucede rápidamente, hay algo que no está bien en la conversión rápida, o están tratando de resolver un problema diferente al que apunta esta pregunta / respuesta.
JosephH
19

En realidad, hay dos lugares donde debe cambiar a la altura de la fila, primero la celda (ya lo cambió) y ahora seleccione la Vista de tabla y verifique el Inspector de tamaño

Kristjan H.
fuente
en vista de tabla (no vista de celda)> en la pestaña del inspector de tamaño> altura de fila
iman kazemayni
Me vuelve loco cada vez que olvido este detalle. ¡No tengo idea de por qué ajustar la celda no actualiza tableView automáticamente cuando se usan guiones gráficos!
Scooter
11

Creo que es un error.

Intente ajustar la altura no mediante el inspector de utilidades, sino arrastrando el mouse directamente al guión gráfico.

Resolví este problema con este método.

Fogni
fuente
10

Si está usando swift, úselo de esta manera. No use el guión gráfico para seleccionar la altura de la fila. Establezca programáticamente la altura de la fila de la tabla de esta manera,

 func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    if indexPath.row == 0 || indexPath.row == 1{
        let cell = self.tableView.dequeueReusableCellWithIdentifier("section1", forIndexPath: indexPath) as! Section1TableViewCell
        self.tableView.rowHeight = 150
        cell.label1.text = "hiiiiii"
        cell.label2.text = "Huiiilllllll"
        return cell

    } else {

        let cell = self.tableView.dequeueReusableCellWithIdentifier("section2", forIndexPath: indexPath) as! Section2TableViewCell
        self.tableView.rowHeight = 60
        cell.label3.text = "llll"
        return cell
    }

}
Chathuranga Silva
fuente
Esto funciona, pero en general puede hacer:cell.sizeToFit(); self.tableView.rowHeight = cell.frame.height
Andy Chou
7

Puede obtener la altura de UITableviewCell (en UITableviewController - celdas estáticas) del guión gráfico con la ayuda de las siguientes líneas.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
   CGFloat height = [super tableView:tableView heightForRowAtIndexPath:indexPath];

    return height;
}
Hiren Panchal
fuente
6

Abra el guión gráfico en la vista XML e intente editar el rowHeightatributo del elemento deseado.

Funcionó para mí cuando traté de configurar la fila personalizada Altura para mi fila prototipo. No funciona a través del inspector, pero a través de XML funciona.

Shtirlic
fuente
1
Hice clic derecho y seleccioné "abrir como" -> "código fuente". El rowHeight ya estaba establecido en 120. Creo que el guión gráfico tiene un error allí e ignora la altura de la celda personalizada.
zirinisp
6

Puede usar un prototipo cellscon una costumbre height, y luego invocar cellForRowAtIndexPath:y devolver su frame.height.:.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self tableView:tableView
                    cellForRowAtIndexPath:indexPath];
    return cell.frame.size.height;
}
Henrik Hartz
fuente
La adición de este método funcionó muy bien y ahora mantiene toda la personalización en el guión gráfico como debería ser.
daspianist
Con mucho, la mejor solución
TheJeff
4

Si desea establecer una altura de fila estática, puede hacer algo como esto:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 120;
}
Victor Palhares
fuente
4

Recientemente he estado luchando con esto. Mi problema fue que las soluciones publicadas anteriormente que usan el heightForRowAtIndexPath:método funcionarían para iOS 7.1 en el Simulador, pero luego han arruinado completamente los resultados simplemente cambiando a iOS 8.1.

Comencé a leer más sobre las celdas de tamaño propio (introducido en iOS 8, leer aquí ). Era evidente que el uso de UITableViewAutomaticDimensionayudaría en iOS 8. Intenté usar esa técnica y eliminé el uso de heightForRowAtIndexPath:y listo, ahora funcionaba perfectamente en iOS 8. Pero entonces iOS 7 no lo fue. ¿Qué iba a hacer yo? Necesitaba heightForRowAtIndexPath:para iOS 7 y no para iOS 8.

Aquí está mi solución (recortada por razones de brevedad) que toma prestada de la respuesta @JosephH publicada anteriormente:

- (void)viewDidLoad {
    [super viewDidLoad];

    self.tableView.estimatedRowHeight = 50.;
    self.tableView.rowHeight = UITableViewAutomaticDimension;

    // ...
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"8.0")) {
        return UITableViewAutomaticDimension;

    } else {
        NSString *cellIdentifier = [self reuseIdentifierForCellAtIndexPath:indexPath];
        static NSMutableDictionary *heightCache;
        if (!heightCache)
            heightCache = [[NSMutableDictionary alloc] init];
        NSNumber *cachedHeight = heightCache[cellIdentifier];
        if (cachedHeight)
            return cachedHeight.floatValue;

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    CGFloat height = cell.bounds.size.height;
    heightCache[cellIdentifier] = @(height);
    return height;
    }
}

- (NSString *)reuseIdentifierForCellAtIndexPath:(NSIndexPath *)indexPath {
    NSString * reuseIdentifier;
    switch (indexPath.row) {
        case 0:
            reuseIdentifier = EventTitleCellIdentifier;
            break;
        case 2:
            reuseIdentifier = EventDateTimeCellIdentifier;
            break;
        case 4:
            reuseIdentifier = EventContactsCellIdentifier;
            break;
        case 6:
            reuseIdentifier = EventLocationCellIdentifier;
            break;
        case 8:
            reuseIdentifier = NotesCellIdentifier;
            break;
        default:
            reuseIdentifier = SeparatorCellIdentifier;
            break;
    }

    return reuseIdentifier;
}

SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO (@ "8.0") en realidad proviene de un conjunto de definiciones de macro que estoy usando y que encontré en alguna parte (muy útil). Se definen como:

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
Speby
fuente
2

El mismo problema ocurrió cuando trabajaba en XCode 9 usando Swift 4 .

Agregue AutoLayout para los elementos de la interfaz de usuario dentro de la celda y la altura de fila de celda personalizada funcionará según lo especificado.

Murari Varma
fuente
1
Hola por favor como hiciste eso
Back Packer
Solo necesita agregar un conjunto de restricciones en la parte inferior de la celda.
Justin
Cuando hago esto y selecciono "automático" en el guión gráfico para la altura de la celda, quiere establecer todas las alturas a 44 en el guión gráfico, aunque parece correcto cuando corro. ¿Cómo puedo hacer que el guión gráfico se muestre correctamente?
David
1

Para celdas dinámicas, rowHeight establecido en UITableView siempre anula la fila de celdas individuales. Simplemente calcule la altura dinámica si el contenido dentro de la fila.

Aks
fuente
0

La única solución real que pude encontrar es esta

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = ...; // Instantiate with a "common" method you'll use again in cellForRowAtIndexPath:
    return cell.frame.size.height;
}

Esto funciona y permite no tener un interruptor horrible / si duplica la lógica ya en el StoryBoard. No estoy seguro sobre el rendimiento, pero supongo que al llegarcellForRow: la celda que ya está inicializado es tan rápido. Por supuesto, probablemente haya daños colaterales aquí, pero parece que funciona bien para mí aquí.

También publiqué esto aquí: https://devforums.apple.com/message/772464

EDITAR: Ortwin Gentz me recordó que heightForRowAtIndexPath:se convocará a todos celdas de TableView, no solo a las visibles. Suena lógico ya que iOS necesita saber la altura total para poder mostrar las barras de desplazamiento correctas. Significa que probablemente esté bien en TableViews pequeños (como 20 celdas), pero olvídalo en un TableView de 1000 celdas.

Además, el truco anterior con XML: igual que el primer comentario para mí. El valor correcto ya estaba allí.

Stuff mc
fuente
0

Agregado como un comentario, pero publicado como una respuesta para la visibilidad:

También parece haber un problema si inicialmente configura una vista de tabla como "celdas estáticas" y luego la cambia a "prototipos dinámicos". Tuve un problema en el que incluso el método de delegado heightForRowAtIndexPathse ignoraba. Primero lo resolví editando directamente el XML del guión gráfico, luego finalmente reconstruí la escena del guión gráfico desde cero, utilizando prototipos dinámicos desde el principio.

Eric Goldberg
fuente
0

Dado que no encontré ninguna solución a este problema a través de Interface Builder, decidí publicar una solución programática para el problema en Swift usando dos celdas dinámicas , a pesar de que la pregunta inicial pedía una solución a través de Interface Builder. Independientemente, creo que podría ser útil para la comunidad de Stack Overflow:

    import UIKit

    enum SignInUpMenuTableViewControllerCellIdentifier: String {
       case BigButtonCell = "BigButtonCell"
       case LabelCell = "LabelCell"
    }

    class SignInUpMenuTableViewController: UITableViewController {
            let heightCache = [SignInUpMenuTableViewControllerCellIdentifier.BigButtonCell : CGFloat(50),
                              SignInUpMenuTableViewControllerCellIdentifier.LabelCell : CGFloat(115)]

    private func cellIdentifierForIndexPath(indexPath: NSIndexPath) -> SignInUpMenuTableViewControllerCellIdentifier {
        if indexPath.row == 2 {
            return SignInUpMenuTableViewControllerCellIdentifier.LabelCell
        } else {
            return SignInUpMenuTableViewControllerCellIdentifier.BigButtonCell
        }
    }

   override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
       return self.heightCache[self.cellIdentifierForIndexPath(indexPath)]!
   }

   ...

  }
Rey Mago
fuente
Todo esto está muy bien, pero a menos que esté leyendo mal el código, simplemente se basa en números mágicos e ignora por completo cualquier valor que establezca en IB para las celdas dinámicas, así que ... no es exactamente una solución a la pregunta de OP
Danny
0

Otra cosa que puede hacer es ir al Esquema del documento, seleccionar la vista de tabla en la que está anidada su celda prototipo. Luego, en el Inspector de tamaño, cambie la altura de fila de la vista de tabla al valor deseado y desactive la casilla Automático.

Amin Rezapour
fuente