L'impostazione dell'altezza della riga di cella personalizzata nello storyboard non risponde


213

Sto cercando di regolare l'altezza della cella per una delle celle nella vista tabella. Sto regolando le dimensioni dall'impostazione "altezza riga" all'interno dell '"ispettore dimensioni" della cella in questione. Quando eseguo l'app sul mio iPhone, la cella ha la dimensione predefinita impostata dalla "dimensione della riga" nella vista tabella.

Se cambio la "dimensione della riga" della vista tabella, allora cambia la dimensione di tutte le celle. Non voglio farlo poiché desidero una dimensione personalizzata solo per una cella. Ho visto molti post che hanno una soluzione programmatica al problema, ma preferirei farlo attraverso lo storyboard, se possibile.

Risposte:


295

Nelle celle dinamiche , rowHeightimpostato su UITableView sostituisce sempre la rigaHeight delle singole celle.

Ma su celle statiche , rowHeightimpostate su singole celle possono sovrascrivere quelle di UITableView.

Non sei sicuro che si tratti di un bug, Apple potrebbe farlo intenzionalmente?


36
Risposta corretta n. 3, e in particolare perché questa risposta si applica a Interface Builder / Storyboard. Se selezioni la cella in IB, la finestra di ispezione Dimensioni mostra l'altezza della riga in alto (con una casella di controllo "personalizzata"), ma se selezioni l'intera vista della tabella la finestra di ispezione Dimensione mostra l'altezza della riga in alto anche lì (nessuna "personalizzata " in questo caso). Come dice pixelfreak, per le celle dinamiche viene utilizzata solo l'impostazione della vista tabella. (Non sono sicuro che sia intenzionale)
Rabarbaro

8
questa risposta implica che la soluzione sarebbe semplicemente quella di cambiare il UITableViewcontenuto da Dynamic Prototypesa Static Cells, l'ho fatto, e il mio intero progetto è esploso .. quasi mi sono ucciso.
circa

4
C'è un modo per recuperare questo numero? L'unico modo per scavare in profondità è immergersi nello storyboard e tirarlo fuori da lì?
Biclops,

Non è sicuramente un bug, perché la tua tabella è dinamica, quindi come può il sistema sapere dove sei andato a utilizzare ciascuna di quelle celle? E quante volte ogni prototipo verrebbe utilizzato.
Vincent Bernier,

Sembra esserci anche un problema se inizialmente si imposta una vista tabella come "celle statiche" e poi si cambia in "prototipi dinamici". Ho avuto un problema in cui anche il metodo delegato per rowHeight veniva ignorato. Prima l'ho risolto modificando direttamente l'XML dello storyboard, quindi alla fine ho ricostruito la scena dello storyboard da zero, utilizzando prototipi dinamici dall'inizio.
Eric Goldberg,

84

Se si utilizza UITableViewController, implementare questo metodo:

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

Nella funzione di una riga puoi scegliere Altezza. Per esempio,

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

In questo esempio, l'altezza della prima riga è di 100 pixel e le altre sono di 60 pixel.

Spero che questo possa aiutarti.


2
Beber, si deve SEMPRE fare entrambe le cose (selezionare Personalizza e modificare il valore Altezza riga nello storyboard e& specificarlo in heightForRowAtIndexPath)?
marciokoko,

Potresti aiutarmi a riguardo? Devo avere celle dinamiche con altezza dinamica, ma se uso questo metodo, indipendentemente da ciò che restituisco, tutte le celle scompaiono. Tranne su iPhone 5 il dispositivo , dove funziona come previsto. Riesci a capire il perché?
Ostmeistro,

34

Per le celle dinamiche, rowHeightimpostare su UITableViewsostituisce sempre le singole celle ' rowHeight.

Questo comportamento è, IMO, un bug. Ogni volta che devi gestire la tua UI in due posti è soggetto ad errori. Ad esempio, se si modifica la dimensione della cella nello storyboard, è necessario ricordare di modificarli anche in heightForRowAtIndexPath:. Fino a quando Apple non risolve il bug, l'attuale soluzione alternativa è quella di eseguire l'override heightForRowAtIndexPath:, ma utilizzare le celle prototipo effettive dallo storyboard per determinare l'altezza anziché utilizzare i numeri magici . Ecco un esempio:

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

Ciò garantirà che eventuali modifiche alle altezze delle celle del tuo prototipo verranno automaticamente rilevate in fase di esecuzione e dovrai solo gestire la tua UI in un unico posto: lo storyboard.


2
Ho pubblicato una risposta con il codice completo inclusa la memorizzazione nella cache; vedi stackoverflow.com/a/16881312/292166
JosephH

1
Wow wow wow! Ho votato a favore della risposta, ma Siate ATTENTI! Questo metodo causa non solo una penalità prestazionale come ha detto @Brennan nei commenti, ma provoca anche una crescente allocazione della memoria ad ogni ricarica. Dati simili a perdite di memoria! È necessario utilizzare la soluzione alternativa di lensovet sopra! Trascorri un giorno per catturare questa perdita di memoria!
skywinder,

Non funziona in Swift, perché cell.bounds.size.height restituisce sempre 0.0
King-Wizard il

1
Le celle non esistono ancora al momento in cui il sistema ha chiamato il metodo tableView: heightForRowAtIndexPath. Dovresti essere in grado di utilizzare indexPath che ti è stato passato per indicizzare il tuo modello di dati e vedere quale tipo di cella verrà utilizzata lì, quindi calcolare l'altezza per quella cella e restituirla. Ciò consente al sistema di disporre le celle nella tabella prima di creare qualsiasi cella.
King-Wizard il

1
Inoltre, non dovresti chiamare il tuo metodo cellForRowAtIndexPath. Le viste tabella hanno un metodo cellForRowAtIndexPath che restituirà una cella per quel percorso indice se è attualmente visibile sullo schermo, ma spesso a quel percorso indice non è associata una cella.
Re-mago,

22

Ho creato il codice suggerito dalle varie risposte / commenti in modo che funzioni per gli storyboard che usano celle prototipo.

Questo codice:

  • Non richiede che l'altezza della cella sia impostata in un punto diverso dall'ovvio posto nello storyboard
  • Memorizza l'altezza per motivi di prestazioni
  • Utilizza una funzione comune per ottenere l'identificatore di cella per un percorso di indice per evitare la logica duplicata

Grazie a Answerbot, Brennan e 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...>

So che questa è una vecchia risposta, ma qualcun altro trova che ciò si traduce in un overflow dello stack? Quando la vista viene caricata, ottengo UISectionRowData refreshWithSection: tableViewRowData: che chiama tableView: heightForRowAtIndexPath: che chiama dequeueReusableCellWithIdentifier: che chiama [UISectionRowData ...], quindi ho un overflow ricorsivo dello stack. Volevo davvero usare questa soluzione, ma non sembra funzionare con iOS7.
sbaker,

Non funziona in Swift, perché cell.bounds.size.height restituisce sempre 0.0
King-Wizard

Le celle non esistono ancora al momento in cui il sistema ha chiamato il metodo tableView: heightForRowAtIndexPath. Dovresti essere in grado di utilizzare indexPath che ti è stato passato per indicizzare il tuo modello di dati e vedere quale tipo di cella verrà utilizzata lì, quindi calcolare l'altezza per quella cella e restituirla. Ciò consente al sistema di disporre le celle nella tabella prima di creare qualsiasi cella.
King-Wizard il

Inoltre, non dovresti chiamare il tuo metodo cellForRowAtIndexPath. Le viste tabella hanno un metodo cellForRowAtIndexPath che restituirà una cella per quel percorso indice se è attualmente visibile sullo schermo, ma spesso a quel percorso indice non è associata una cella.
King-Wizard il

@ snow-tiger Non ci ho provato in fretta, ma non capisco i tuoi commenti. Non 'chiamo il mio metodo cellForRowAtIndexPath [mio], ed è deliberato che non lo faccia. Sono anche consapevole del fatto che le celle non esistono al momento della visualizzazione tabella: heightForRowAtIndexPath viene chiamato, questo è l'intero punto di questo codice e perché utilizza dequeueReusableCellWithIdentifier per ottenere una cella. Il codice pubblicato in questa risposta funziona. O c'è qualcosa in più che sta succedendo rapidamente, c'è qualcosa che non va nella conversione rapida, o stanno cercando di risolvere un problema diverso che questa domanda / risposta ha come obiettivo.
JosephH,

19

In realtà ci sono due posti in cui è necessario modificare l'altezza della riga, prima la cella (l'hai già modificata) e ora selezionare la vista tabella e controllare la finestra di ispezione delle dimensioni


in vista tabella (non visualizzazione cellulare)> nella scheda Impostazioni dimensioni> altezza riga
iman kazemayni,

Mi fa impazzire ogni volta che dimentico questo dettaglio. Non ho idea del perché la regolazione della cella non aggiorni automaticamente tableView quando si usano gli storyboard!
Scooter

11

Penso che sia un bug.

Prova a regolare l'altezza non tramite la finestra di ispezione Utility ma trascinando il mouse direttamente sullo storyboard.

Ho risolto questo problema con questo metodo.


10

Se stai usando swift, usa così. Non utilizzare lo storyboard per selezionare l'altezza della riga. Imposta in modo programmatico l'altezza della riga della tabella in questo modo,

 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
    }

}

Funziona, ma più in generale puoi fare:cell.sizeToFit(); self.tableView.rowHeight = cell.frame.height
Andy Chou

7

Puoi ottenere l'altezza di UITableviewCell (in UITableviewController - celle statiche) dallo storyboard con l'aiuto delle seguenti righe.

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

    return height;
}

6

Apri lo storyboard nella vista XML e prova a modificare l' rowHeightattributo dell'elemento desiderato.

Ha funzionato per me quando ho cercato di impostare rowHeight personalizzato per la mia riga prototipata. Non funziona tramite inspector, ma tramite XML funziona.


1
Ho cliccato col tasto destro e ho selezionato "apri come" -> "codice sorgente". RowHeight era già impostato su 120. Credo che lo storyboard abbia un bug lì e ignori l'altezza della cella personalizzata.
zirinisp,

6

È possibile utilizzare il prototipo cellscon un'abitudine height, quindi richiamare cellForRowAtIndexPath:e restituire il suo frame.height.:.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self tableView:tableView
                    cellForRowAtIndexPath:indexPath];
    return cell.frame.size.height;
}

L'aggiunta di questo metodo ha funzionato molto bene e ora mantiene tutta la personalizzazione nello storyboard come dovrebbe appartenere.
Daspianista,

Di gran lunga la soluzione migliore
TheJeff

4

Se vuoi impostare un'altezza di riga statica, puoi fare qualcosa del genere:

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

4

Di recente ho lottato con questo. Il mio problema era che le soluzioni pubblicate sopra usando il heightForRowAtIndexPath:metodo funzionavano per iOS 7.1 nel Simulatore ma poi hanno rovinato completamente i risultati semplicemente passando a iOS 8.1.

Ho iniziato a leggere di più sulle celle auto-dimensionanti (introdotte in iOS 8, leggi qui ). Era evidente che l'uso di UITableViewAutomaticDimensionavrebbe aiutato in iOS 8. Ho provato a usare quella tecnica e ho eliminato l'uso di heightForRowAtIndexPath:e voilà, ora funzionava perfettamente in iOS 8. Ma poi iOS 7 non lo era. Cosa dovevo fare? Avevo bisogno heightForRowAtIndexPath:di iOS 7 e non di iOS 8.

Ecco la mia soluzione (rifilata per brevità) che prende in prestito dalla risposta @JosephH pubblicata sopra:

- (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") proviene effettivamente da una serie di definizioni macro che sto usando che ho trovato da qualche parte (molto utile). Sono definiti come:

#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)

2

Lo stesso problema si è verificato quando si lavora su XCode 9 utilizzando Swift 4 .

Aggiungi AutoLayout per gli elementi dell'interfaccia utente all'interno della cella e l'altezza della riga della cella personalizzata funzionerà di conseguenza come specificato.


1
Ciao, per favore, come hai fatto?
Back Packer

Devi solo aggiungere un set di vincoli nella parte inferiore della cella.
Justin,

Quando lo faccio e seleziono "automatico" nello storyboard per l'altezza della cella, vuole impostare tutte le altezze su 44 nello storyboard anche se sembra corretto quando corro. Come posso visualizzare correttamente lo storyboard?
David,

1

Per le celle dinamiche, rowHeight impostato su UITableView sostituisce sempre rowHeight delle singole celle. Basta calcolare l'altezza dinamica se il contenuto all'interno della riga.


0

L'unica vera soluzione che ho trovato è questa

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

Funziona e consente di non avere un interruttore orribile / se si duplica la logica già in StoryBoard. Non sono sicuro delle prestazioni, ma immagino che quando si arriva nella cellForRow:cella essendo già inattivato sia altrettanto veloce. Naturalmente ci sono probabilmente danni collaterali qui, ma sembra che funzioni bene per me qui.

Ho anche pubblicato questo qui: https://devforums.apple.com/message/772464

EDIT: Ortwin Gentz mi ha ricordato che heightForRowAtIndexPath:verrà chiamato per tutte le celle di TableView, non solo quelle visibili. Sembra logico poiché iOS deve conoscere l'altezza totale per poter mostrare le barre di scorrimento giuste. Significa che probabilmente va bene su piccole TableView (come 20 celle) ma dimenticalo su un TableView 1000 celle.

Inoltre, il trucco precedente con XML: lo stesso del primo commento per me. Il valore corretto era già lì.


0

Aggiunto come commento, ma pubblicando come risposta per la visibilità:

Sembra esserci anche un problema se inizialmente si imposta una vista tabella come "celle statiche" e poi si cambia in "prototipi dinamici". Ho avuto un problema in cui anche il metodo delegato heightForRowAtIndexPathveniva ignorato. Prima l'ho risolto modificando direttamente l'XML dello storyboard, quindi alla fine ho ricostruito la scena dello storyboard da zero, utilizzando prototipi dinamici dall'inizio.


0

Dato che non ho trovato alcuna soluzione a questo problema tramite Interface Builder, ho deciso di pubblicare una soluzione programmatica al problema in Swift utilizzando due celle dinamiche , anche se la domanda iniziale richiedeva una soluzione tramite Interface Builder. Indipendentemente da ciò, penso che potrebbe essere utile per la comunità 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)]!
   }

   ...

  }

Questo va bene e va bene, ma a meno che non stia leggendo male il codice, si basa semplicemente su numeri magici e ignora completamente qualsiasi valore impostato in IB per le celle dinamiche, quindi ... non esattamente una soluzione alla domanda di OP
Danny

0

Un'altra cosa che puoi fare è andare alla struttura del documento, selezionare la vista tabella in cui è nidificata la cella del prototipo. Quindi, nella finestra di ispezione Dimensioni, modifica l'altezza della riga della vista tabella al valore desiderato e deseleziona la casella Automatico.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.