2014-09-20 8 views
54

In questa app iOS 8 sto creando, ho una vista tabella e ho bisogno di ridimensionarli autonomamente. L'ho implementato usando Auto Layout e funziona. Quasi. Ecco come appare ora.UILabels multipli all'interno di un auto-dimensionamento UITableViewCell

enter image description here

Ci sono 3 etichette all'interno di una cellula. Etichetta principale che contiene il testo di lorem ipsum. Sottotitolo che ha la stringa di numeri (Quelli sono due etichette separate. Potrebbe essere fonte di confusione perché hanno lo stesso colore.) Quindi la terza etichetta con il piccolo testo nero.

La prima etichetta si ridimensiona correttamente senza alcun problema e la seconda etichetta si sposta su e giù di conseguenza. Ma il problema è con la terza piccola etichetta. Come puoi vedere, non si ridimensiona per adattarsi a tutto il testo.

Ora succede una cosa strana. Lo trasformo in orizzontale e qui è.

enter image description here

Poiché non v'è spazio l'etichetta sta visualizzando l'intero testo sua suppone. Belle. Poi lo restituisco al ritratto.

enter image description here

Ora la piccola etichetta stessa ha ridimensionato per adattarsi a tutti i suoi testi, ma trabocca i confini cellule. Ho provato a rendere la cella più grande ma non ha funzionato. Dal momento che questo è un auto-dimensionamento delle celle, non penso che sia il modo corretto anche.

Non ricevo alcun errore o avviso sui miei vincoli di layout automatico.

enter image description here

ho impostare queste due righe di codice nel metodo viewDidLoad().

tableView.estimatedRowHeight = 100 
tableView.rowHeight = UITableViewAutomaticDimension 

Qualcuno può dirmi per favore cosa potrei fare di sbagliato qui?

Dal momento che è difficile rispondere semplicemente guardando le immagini e non ho altro codice da inserire accanto allo snippet sopra riportato, ho caricato un progetto Xcode eseguibile che dimostra il problema here. (Esistono 2 celle personalizzate. Fondamentalmente è la stessa cella, solo l'altezza è aumentata nella seconda.)

Mi sono divertito con i vincoli di layout automatico ma non riesco a farlo funzionare. Qualsiasi aiuto sarebbe apprezzato.

Grazie.


UPDATE:

Con l'aiuto di questo tutorial ho trovato alcune indicazioni utili. In base ad esso, ogni sottoview dovrebbe avere vincoli che ne vincolano tutti i lati e ci dovrebbero essere dei vincoli che vanno dall'alto verso il basso che aiuta il layout automatico a calcolare l'altezza della cella. Nel mio post originale avevo spazi verticali tra ogni etichetta, quindi penso che sia il motivo per cui il layout automatico non è in grado di calcolare l'altezza corretta.

Quindi ho apportato alcune modifiche.

  • Ho ridotto lo spazio verticale tra le etichette a 0 e impostato i vincoli di spazio verticale tra le etichette superiore e centrale e le etichette media e inferiore.
  • Ho aggiunto i vincoli iniziali, superiori e finali all'etichetta superiore.
  • In arrivo e in coda all'etichetta centrale.
  • In arrivo, in basso, in coda all'etichetta inferiore.

Ora ecco un'altra parte strana. Quando lo eseguo per la prima volta, il problema del ritaglio dell'etichetta in fondo è ancora lì.

enter image description here

Ma se ruoto il dispositivo al paesaggio e ridare la parola a ritratto, tutte le tutte le cellule vengono ridimensionate correttamente per adattarsi entrambe le etichette!

enter image description here

Ancora non riesco a capire perché questo non accada in un primo momento, però. Il progetto Xcode aggiornato è here.

+3

La tabella sta trovando troppo difficile stimare l'altezza esatta del testo nelle etichette prima della la vista appare, ma sta andando piuttosto bene lo stesso. Ho scoperto che se sposti tableView.estimatedRowHeight = 100 per viewDidAppear allora questo risolve il problema che stai vedendo – sketchyTech

+1

... o meglio, sembra che la tabella stia usando l'altezza stimata come scusa per non eseguire un calcolo esatto al primo caricamento la tavola. Ma se stimaRowHeight viene spostato come descritto, risolve il problema che stai vedendo. La sua rimozione completa crea problemi di ridisegno durante la rotazione. – sketchyTech

+0

@GoodbyeStackOverflow Ho fatto come suggerito ma sto affrontando alcuni problemi. [Ecco come appare quando lo esegui] (http://i.imgur.com/1CNGVgR.png) e [quando scorri verso il basso] (http://i.imgur.com/lH7hasO.png).Ho anche applicato lo stesso al mio secondo esempio e [questo è il risultato] (http://i.imgur.com/AWg9Yib.png). Sembra buono nelle prime 3 celle ma si noti che è stato incasinato nell'ultima cella. – Isuru

risposta

75

Il problema qui è con la proprietà le etichette multi-linea preferredMaxLayoutWidth. Questa è la proprietà che dice all'etichetta quando dovrebbe avvolgere le parole. Deve essere impostato correttamente affinché intrinsicContentSize di ciascuna etichetta abbia l'altezza corretta, che in definitiva è ciò che verrà utilizzato dal layout automatico per determinare l'altezza della cella.

Xcode 6 Interface Builder ha introdotto una nuova opzione per impostare questa proprietà su Automatico. Sfortunatamente, ci sono alcuni bug gravi (da Xcode 6.2/iOS 8.2) in cui questo non è impostato correttamente/automaticamente quando si carica una cella da un pennino o uno Storyboard.

Per ovviare a questo problema, è necessario impostare preferredMaxLayoutWidth esattamente uguale alla larghezza finale dell'etichetta una volta visualizzata nella visualizzazione tabella. In effetti, vogliamo fare quanto segue prima di restituire il cellulare da tableView:cellForRowAtIndexPath::

cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame) 
cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame) 
cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame) 

La ragione per cui solo l'aggiunta di questo codice da sola non funziona è perché quando queste 3 righe di codice vengono eseguite in tableView:cellForRowAtIndexPath:, stiamo usando il larghezza di ciascuna etichetta per impostare preferredMaxLayoutWidth - tuttavia, se si controlla la larghezza delle etichette in questo momento, la larghezza dell'etichetta è completamente diversa da quella che verrà visualizzata una volta che la cella è stata visualizzata e le sue sottoview sono state posizionate su.

Come si ottengono le larghezze dell'etichetta per essere accurate a questo punto, in modo che riflettano la loro larghezza finale? Ecco il codice che fa sì che tutto si concentri:

// Inside of tableView:cellForRowAtIndexPath:, after dequeueing the cell 

cell.bounds = CGRect(x: 0, y: 0, width: CGRectGetWidth(tableView.bounds), height: 99999) 
cell.contentView.bounds = cell.bounds 
cell.layoutIfNeeded() 

cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame) 
cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame) 
cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame) 

OK, quindi cosa stiamo facendo qui? Bene, noterai che ci sono 3 nuove linee di codice aggiunte. Innanzitutto, è necessario impostare questa larghezza della cella della vista tabella in modo che corrisponda alla larghezza effettiva della vista tabella (ciò presuppone che la vista tabella sia già stata impostata e abbia la larghezza finale, che dovrebbe essere il caso). Stiamo effettivamente rendendo la larghezza della cella corretta in anticipo, poiché la visualizzazione della tabella lo farà alla fine.

Si noterà anche che stiamo usando 99999 per l'altezza. Di che si tratta? Questa è una soluzione semplice per il problema discusso in dettaglio here, dove se i vincoli richiedono più spazio verticale dell'altezza corrente del contentView della cella, si ottiene un'eccezione di vincolo che in realtà non indica alcun problema reale. L'altezza della cella o di qualsiasi sua sottoview in questo momento non ha importanza, perché ci interessa solo ottenere le larghezze finali per ogni etichetta.

Successivamente, ci assicuriamo che il contentView della cella abbia le stesse dimensioni che abbiamo appena assegnato alla cella stessa, impostando i limiti di contentView in modo che corrispondano ai limiti della cella. Questo è necessario perché tutti i vincoli di layout automatico che hai creato sono relativi a contentView, quindi contentView deve essere della dimensione corretta per poterli risolvere correttamente. Semplicemente impostando manualmente le dimensioni della cella, non ridimensiona automaticamente ContentView in modo che corrisponda.

Infine, forziamo un passaggio di layout sulla cella, che consentirà al motore di layout automatico di risolvere i vincoli e aggiornare i frame di tutte le sottoview. Poiché la cella & contentView ora ha la stessa larghezza che avranno in fase di esecuzione nella vista tabella, anche le larghezze dell'etichetta saranno corrette, il che significa che l'preferredMaxLayoutWidth impostato su ciascuna etichetta sarà accurato e farà sì che l'etichetta si avvolga al momento giusto , che ovviamente significa che le altezze delle etichette verranno impostate correttamente quando la cella viene utilizzata nella vista tabella!

Questo è sicuramente un bug di Apple in UIKit che dobbiamo risolvere per ora (quindi per favore fare file bug reports con Apple in modo da dare la priorità a una correzione!).

Un'ultima nota: questa soluzione si guasta se la larghezza della tabella contentView della cella di visualizzazione tabella non estende l'intera larghezza della vista tabella, ad esempio quando sulla destra è presente un indice di sezione. In questo caso, è necessario fare in modo che si prende manualmente questo in considerazione quando si imposta la larghezza della cella - potrebbe essere necessario codificare questi valori, qualcosa di simile:

let cellWidth = CGRectGetWidth(tableView.bounds) - kTableViewSectionIndexWidth 
cell.bounds = CGRect(x: 0, y: 0, width: cellWidth, height: 99999) 
+0

@Isuru Le eccezioni di vincoli insoddisfacibili che si ottengono dopo aver forzato un passaggio di layout sulla cella non sono probabilmente un problema reale e possono essere risolti facilmente. Vedi [questo thread] (https://github.com/smileyborg/TableViewCellWithAutoLayout/issues/16) per una discussione completa del problema. – smileyborg

+0

Grazie. Ho trascorso 2 giorni a provare tutto ciò che riuscivo a trovare per ridimensionare correttamente le etichette. La cosa strana è che alcuni hanno ridimensionato, ma altri design di celle no. Non sono riuscito a trovare una differenza nei vincoli o in altre impostazioni. Tuttavia, la tua soluzione ha funzionato alla grande. Ho creato una sottoclasse UILabel e ho inserito l'impostazione preferredMaxLayoutWidth all'interno di setBounds per evitare riferimenti rigidi a UILabels nel controller di visualizzazione tabella. –

+2

Sei il mio eroe! – pr1001

4

Hai due problemi qui.

1/In questo momento, utilizzando Visual Formato Lingua, i vincoli verticali del vostro cellule possono essere tradotte in questo modo:

Cell: "V:|-(10)-[nameLabel]-(67)-|" 

Quindi, è possibile impostare un secondo gruppo di vincoli:

Cell: "V:|-(10)-[nameLabel]-(8)-[pnrLabel]-(2)-[actionsLabel]" 

Quei due i gruppi di vincoli non possono mescolarsi bene e rivelano la loro ambiguità con il tuo secondo problema.

2/Per alcuni motivi, actionsLabel è limitato a una riga quando si avvia l'app. Quindi, quando si ruota il dispositivo in modalità orizzontale, actionsLabel accetta di essere visualizzato con due o più righe. Quindi, quando si ruota il dispositivo in modalità verticale, actionsLabel continua a visualizzare due o più righe. Tuttavia, poiché actionsLabel non fa realmente parte dei limiti di altezza della cella, si sovrappone ai limiti della cella.

Se si desidera risolvere tutti questi problemi, è consigliabile innanzitutto ricostruire il file xib da zero. Questo curerà il tuo comportamento strano actionsLabel (due linee o più solo quando si ruota il dispositivo).

Poi, si dovrà definire i vincoli in questo modo:

Cell: "V:|-(10)-[nameLabel(>=21)]-(8)-[pnrLabel(>=21)]-(2)-[actionsLabel(>=21)]-(10)-|" 

Naturalmente, è possibile definire altri vincoli altezza minima per voi le etichette di (>=21). Allo stesso modo, il margine inferiore può essere impostato su un valore diverso da -(10)-.

Addendum

Al fine di rispondere alla tua domanda, ho creato un semplice progetto con il precedente modello di vincoli nel mio file .xib. L'immagine seguente può aiutarti a costruire i tuoi limiti.

enter image description here

+0

Ottima risposta concisa. – memmons

+0

Grazie per la risposta dettagliata. Sono riuscito a farlo funzionare, ma c'è un piccolo problema. Poiché la larghezza di queste etichette è fissa, quando si ruota lo sfondo dello schermo non si adattano per riempire lo spazio extra. C'è un modo per contrastare questo? Aggiungere lo spazio finale a Superview su quelle etichette non sembra funzionare. – Isuru

+0

Ho impostato limiti di larghezza per ciascuna etichetta nel mio progetto di test a scopo di test. Nella tua app, non impostare i vincoli di larghezza, ma imposta Trailing Space su Contrints margini Superview per ogni etichetta. Non impostare i vincoli di larghezza e lo spazio trailing a Sovrapponi margini di sovrastampa per ogni etichetta allo stesso tempo: devi scegliere tra l'uno o l'altro. –

48

ho incontrato lo stesso problema come tu e io abbiamo trovato una soluzione semplice per risolverlo.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    // dequeue cell... 

    // do autolayout staffs...or if the autolayout rule has been set in xib, do nothing 

    [cell layoutIfNeeded]; 

    return cell; 
} 

E l'auto-dimensionamento ha funzionato bene. Nel mio codice, ho disposto due etichette in verticale, entrambe sono di altezza dinamica. L'altezza della cella è impostata correttamente per contenere le due etichette.

+2

Questa è la migliore risposta. La mia risposta accettata per impostare preferredMaxLayoutWidth ha ancora problemi nella mia esperienza. –

+0

sheesh, ho cercato dappertutto una risposta. tutte queste soluzioni complicate e ineleganti. qui, una riga di codice. fatto ... – zillaofthegods

+2

Purtroppo ho trovato che questo non ha funzionato per me, mentre la soluzione di smileyborg ha fatto. – pr1001

6

Supponendo che non ci siano errori con i vincoli come altri hanno suggerito, questo problema sembra derivare dall'uso di un UILabel che consente più linee in combinazione con un UITableViewCellAccessory. Quando iOS espone la cella e determina l'altezza, non tiene conto della variazione di larghezza dell'offset che si verifica a causa di questo accessorio e si ottiene il troncamento dove non ci si aspetterebbe.

Supponendo che si desidera che l'UILabel di estendere l'intera larghezza della visualizzazione dei contenuti, ho scritto un metodo che risolve questo per tutte le dimensioni dei caratteri

-(void)fixWidth:(UILabel *)label forCell:(UITableViewCell *)cell { 
    float offset = 0; 
    switch ([cell accessoryType]) { 
     case UITableViewCellAccessoryCheckmark: 
      offset = 39.0; 
      break; 
     case UITableViewCellAccessoryDetailButton: 
      offset = 47.0; 
      break; 
     case UITableViewCellAccessoryDetailDisclosureButton: 
      offset = 67.0; 
      break; 
     case UITableViewCellAccessoryDisclosureIndicator: 
      offset = 33.0; 
      break; 
     case UITableViewCellAccessoryNone: 
      offset = 0; 
      break; 
    } 
    [label setPreferredMaxLayoutWidth:CGRectGetWidth([[self tableView]frame]) - offset - 8]; 
} 

In poche parole questo nella vostra cellForRowAtIndexPath

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    #Setup the cell 
    ... 
    // Fix layout with accessory view 
    [self fixWidth:[cell label] forCell:cell]; 
    return cell; 
} 

Effettuate questa operazione per le etichette che avranno più righe per regolare correttamente la larghezza e quindi ricalcolare le altezze appropriate. Funziona anche con dimensioni di carattere dinamiche.

Come menzionato da smileyborg, se non si stesse utilizzando tutta la larghezza del contentView, si potevano fare riferimento ai vincoli e sottrarli anche dalla larghezza.

Modifica: in precedenza avevo eseguito 'layoutIfNeeded' sulla cella ma questo stava creando problemi di prestazioni e non sembrava essere necessario comunque. Rimozione non ha causato alcun problema per me.

1

Ho provato la soluzione molto semplice ed elegante di "fogisland" - e non ha funzionato. Fortunatamente ho scoperto che una linea aggiuntiva lo fa funzionare in tutte le direzioni. Basta dire il sistema che non solo suggerisce un nuovo layout (layoutIfNeeded), è esplicitamente Ask For It (setNeedLayout)

cell.setNeedsLayout() 
    cell.layoutIfNeeded() 
    return cell 
Problemi correlati