iOS: UILabel multilinea in layout automatico


183

Sto riscontrando problemi nel tentativo di ottenere alcuni comportamenti di layout di base con Auto Layout. Il mio controller di visualizzazione è simile a questo in IB:

inserisci qui la descrizione dell'immagine

L'etichetta superiore è l'etichetta del titolo, non so quante righe saranno. Ho bisogno dell'etichetta del titolo per visualizzare tutte le righe di testo. Ho anche bisogno che le altre due etichette e l'immagine piccola siano disposte proprio sotto il titolo, per quanto alte possano essere. Ho impostato vincoli di spaziatura verticale tra le etichette e l'immagine piccola, nonché un vincolo di spaziatura superiore tra l'etichetta del titolo e la sua superview e un vincolo di spaziatura inferiore tra l'immagine piccola e la sua superview. L'UIView bianco non ha vincoli di altezza, quindi dovrebbe allungarsi verticalmente per contenere le sue sottoview. Ho impostato il numero di righe per l'etichetta del titolo su 0.

Come posso ridimensionare l'etichetta del titolo per adattarla al numero di righe richieste dalla stringa? La mia comprensione è che non posso usare i metodi setFrame perché sto usando Auto Layout. E devo usare il layout automatico perché ho bisogno che quelle altre viste rimangano sotto l'etichetta del titolo (quindi i vincoli).

Come posso farlo accadere?


3
Sto anche combattendo con un problema simile. Ma sto ancora lottando per ottenere l'etichetta superiore per regolare la sua altezza in modo dinamico per adattarlo al contenuto. Come ci sei riuscito?
jbandi,

1
Ti preghiamo di considerare la risposta di @ mwhuss come accettata.
jemmons,

1
Hai raggiunto il risultato richiesto?
Aniruddha,

controlla questo, non è necessario aggiungere una sola riga di codice stackoverflow.com/a/36862795/4910767
Badal Shah,

Risposte:


254

L'utilizzo -setPreferredMaxLayoutWidthsu UILabele autolayout dovrebbe gestire il resto.

[label setPreferredMaxLayoutWidth:200.0];

Consulta la documentazione di UILabel su FavoriteMaxLayoutWidth .

Aggiornare:

È sufficiente impostare il heightvincolo nello storyboard su Greater than or equal to, non è necessario impostarePreferredMaxLayoutWidth.


8
Bello, comunque per farlo nel generatore di interfaccia?
Sjoerd Perfors il

12
Imposta anche il vincolo di altezza in IB su Maggiore o uguale a ".
jowie

10
Promemoria: ricordarsi di impostare la proprietà numberOfLines.
Li Fumin,

2
Sì, usa quello che vuoi che sia la larghezza massima.
mwhuss,

4
Vedi stackoverflow.com/a/29180323/1529675 per come determinare preferredMaxLayoutWidthe vedere devetc.org/code/2014/07/07/auto-layout-and-views-that-wrap.html per un breve ma informativo resoconto, completo di GIF animate (non il mio commento, l'ho trovato tramite Google)
tboyce12

213

Espandi il numero di righe del set di etichette su 0 e, cosa ancora più importante, per il layout automatico imposta l'altezza su> = x. Il layout automatico farà il resto. È inoltre possibile contenere gli altri elementi in base all'elemento precedente per posizionarli correttamente.

layout automatico


Se questo metodo non funziona per te, consulta la risposta di Cory Imdieke per assicurarti che le visualizzazioni successive non vietino al layout automatico di ridimensionare la (possibilmente) vista multilinea.
Tom Howard,

1
Questa è l'unica risposta che ha funzionato per me all'interno di una cella di visualizzazione tabella. Funziona anche con un numero fisso di righe (anziché 0 / illimitato)
bgolson

Questo non ha funzionato per me con un set di larghezza massima preferito esplicito, quindi assicurati che sia impostato su IB in automatico quando usi questa tecnica. Saluti
jmc,

L'impostazione della larghezza massima automatica è disponibile solo in iOS8. Inoltre, dal mio punto di vista, regolerà automaticamente l'altezza e nella mia esperienza non è necessario impostare un vincolo di altezza per farlo funzionare. Questo ha funzionato per me usando una larghezza esplicita.
Tony,

1
Questo non ha funzionato per me. Credo che stackoverflow.com/a/13032251/1529675 sia la risposta corretta.
tboyce12,

48

Fonte: http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html

Contenuto intrinseco Dimensione del testo multilinea

Le dimensioni intrinseche del contenuto di UILabel e NSTextField sono ambigue per il testo su più righe. L'altezza del testo dipende dalla larghezza delle linee, che deve ancora essere determinata quando si risolvono i vincoli. Per risolvere questo problema, entrambe le classi hanno una nuova proprietà chiamata favoriteMaxLayoutWidth, che specifica la larghezza massima della linea per il calcolo della dimensione del contenuto intrinseco.

Dato che di solito non conosciamo questo valore in anticipo, è necessario adottare un approccio in due fasi per farlo bene. Prima lasciamo che Auto Layout faccia il suo lavoro, quindi usiamo il frame risultante nel passaggio layout per aggiornare la larghezza massima preferita e attivare nuovamente il layout.

- (void)layoutSubviews
{
    [super layoutSubviews];
    myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
    [super layoutSubviews];
}

La prima chiamata a [super layoutSubviews] è necessaria affinché l'etichetta ottenga il set di frame, mentre la seconda chiamata è necessaria per aggiornare il layout dopo la modifica. Se omettiamo la seconda chiamata, viene visualizzato un errore NSInternalInconsistencyException, poiché abbiamo apportato modifiche al passaggio del layout che richiedono l'aggiornamento dei vincoli, ma non abbiamo attivato nuovamente il layout.

Possiamo anche farlo in una sottoclasse di etichette stessa:

@implementation MyLabel
- (void)layoutSubviews
{
    self.preferredMaxLayoutWidth = self.frame.size.width;
    [super layoutSubviews];
}
@end

In questo caso, non è necessario chiamare prima [super layoutSubviews], perché quando viene chiamato layoutSubviews, abbiamo già un frame sull'etichetta stessa.

Per apportare questa modifica dal livello del controller della vista, ci colleghiamo a viewDidLayoutSubviews. A questo punto i frame del primo passaggio Layout automatico sono già impostati e possiamo usarli per impostare la larghezza massima preferita.

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
    [self.view layoutIfNeeded];
}

Infine, assicurati di non avere un limite di altezza esplicito sull'etichetta che abbia una priorità più alta rispetto alla priorità di resistenza alla compressione del contenuto dell'etichetta. Altrimenti trionferà sull'altezza calcolata del contenuto. Assicurati di controllare tutti i vincoli che possono influenzare l'altezza dell'etichetta.


grazie grazie grazie non solo per il codice, ma soprattutto per la spiegazione e gli esempi di come farlo non solo in una sottoclasse, ma anche dal controller di visualizzazione.
djibouti33,

sto tentando questo nel mio controller di visualizzazione e viewDidLayoutSubviews viene chiamato dopo viewWillAppear e viewDidAppear. Dopo viewDidAppear, c'è un lampo quando le etichette vengono ridimensionate correttamente. c'è un modo per evitarlo? c'è un modo per completare il ridimensionamento prima che l'utente veda qualcosa? nel mio caso, le mie etichette multilinea sono in un tableHeaderView e quindi sto anche ridimensionando l'altezza della vista dell'intestazione in base alle etichette usando questo esempio ( stackoverflow.com/questions/20982558/… )
djibouti33

@ djibouti33 puoi fornire il codice di esempio minimo (ad es. nel formato di repo github) in modo da poter facilmente verificare il tuo problema?
Anton Matosov,

Grazie @Anton. Da allora il nostro design è passato da UITableView a UICollectionView e fortunatamente non ho visto questo comportamento. Se riesco ad accedere alla mia cronologia git per creare un piccolo progetto di esempio, ti farò sapere.
djibouti33,

Un problema per me da quando ho iniziato a correre su una sim iOS 9 ma quando ho provato su iOS 8, il problema è iniziato. Devo impostare manualmente la larghezza massima.
naz,

17

Stavo solo combattendo con questo scenario esatto, ma con alcune altre viste che dovevano essere ridimensionate e spostate verso il basso se necessario. Mi stava facendo impazzire, ma alla fine l'ho capito.

Ecco la chiave: Interface Builder preferisce aggiungere ulteriori vincoli quando aggiungi e sposti viste e potresti non accorgertene. Nel mio caso, avevo una vista a metà strada che aveva un ulteriore vincolo che specificava la dimensione tra esso e la sua superview, sostanzialmente bloccandola a quel punto. Ciò significava che nulla al di sopra di esso poteva ridimensionarsi perché andava contro quel vincolo.

Un modo semplice per capire se questo è il caso è provare a ridimensionare manualmente l'etichetta. IB ti consente di coltivarlo? In tal caso, le etichette sottostanti si muovono come previsto? Assicurati di averli controllati entrambi prima di ridimensionare per vedere come i tuoi vincoli sposteranno le tue viste:

Menu IB

Se la vista è bloccata, seguire le viste sottostanti e assicurarsi che uno di essi non disponga di uno spazio superiore per il vincolo di superview. Quindi assicurati che l'opzione del numero di righe per l'etichetta sia impostata su 0 e che si occupi del resto.


Sì, dopo averci giocato molto sono stato in grado di ottenere l'effetto che volevo. Devi solo pensare molto attentamente ai vincoli che desideri. Non aiuta che IB stia costantemente gettando vincoli che non ti aspetti.
James Harpe,

5

Trovo che sia necessario quanto segue:

  • Un grande vincolo
  • Un vincolo principale (ad es. Lato sinistro)
  • Un vincolo finale (ad es. Lato destro)
  • Imposta la priorità di abbracciamento del contenuto, orizzontale su bassa, in modo da riempire lo spazio dato se il testo è breve.
  • Imposta la resistenza alla compressione del contenuto, da orizzontale a basso, in modo da avvolgerlo invece di cercare di allargarlo.
  • Imposta il numero di righe su 0.
  • Imposta la modalità di interruzione di riga su a capo automatico.

Questo dovrebbe funzionare in xcode9. Non ho dovuto fare nulla con il vincolo di altezza dell'etichetta (non ne ho): funziona appena fuori dalla scatola una volta che l'etichetta sarà stata vincolata (trascinamento, in testa, in alto e in basso). uikit fa il resto
Anton Tropashko,

La priorità di abbracciare il contenuto era il mio problema. Grazie per avermi informato di quella proprietà e della resistenza alla compressione. Mi hai aiutato a risolvere un problema di un'ora.
David Gay

0

Nessuna delle diverse soluzioni trovate nei molti argomenti sull'argomento ha funzionato perfettamente per il mio caso (x etichette multilinea dinamiche nelle celle di visualizzazione dinamica della tabella).

Ho trovato un modo per farlo:

Dopo aver impostato i vincoli sull'etichetta e impostato la proprietà multilinea su 0, creare una sottoclasse di UILabel; Ho chiamato il mio AutoLayoutLabel:

@implementation AutoLayoutLabel

- (void)layoutSubviews{
    [self setNeedsUpdateConstraints];
    [super layoutSubviews];
    self.preferredMaxLayoutWidth = CGRectGetWidth(self.bounds);
}

@end

0

Ho un UITableViewCell che ha un'etichetta a capo automatico. Ho lavorato alla disposizione del testo come segue.

1) Impostare i vincoli UILabel come segue.

inserisci qui la descrizione dell'immagine

2) Set n. di righe a 0.

3) Aggiunto vincolo di altezza UILabel a UITableViewCell.

@IBOutlet weak var priorityLabelWidth: NSLayoutConstraint!

4) Su UITableViewCell:

priorityLabel.sizeToFit()
priorityLabelWidth.constant = priorityLabel.intrinsicContentSize().width+5

-12

Un modo per farlo ... Man mano che la lunghezza del testo aumenta, prova a cambiare (ridurre) la dimensione del carattere del testo dell'etichetta usando

Label.adjustsFontSizeToFitWidth = YES;

1
Voglio che le diverse istanze del controller di visualizzazione abbiano un aspetto coerente, quindi non voglio cambiare la dimensione del carattere.
James Harpe,
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.