2013-03-19 13 views
15

Così ho impostato la mia vista in IB in modo tale che questa etichetta di testo si allinea con la parte superiore della miniatura tramite vincoli.Allinea verticalmente testo UILabel con vincoli e no wrap (layout automatico, riga singola)

enter image description here

Tuttavia, come sappiamo, non è possibile allineare verticalmente il testo in un'UILabel. Il mio testo aggiorna la dimensione del carattere in base alla lunghezza del contenuto. Il testo a dimensioni intere è ottimo, mentre il testo di dimensioni ridotte è significativamente più basso nella vista.

enter image description here

Il existing solution coinvolge sia chiamando sizeToFit o l'aggiornamento del telaio della UILabel per abbinare l'altezza del testo. Sfortunatamente quest'ultima soluzione (seppur brutta) non funziona bene con vincoli in cui non si suppone che si aggiorni il frame. La prima soluzione fondamentalmente non funziona quando è necessario avere il testo autoshrink fino a quando non tronca. (Quindi non funziona con un numero limitato di linee e autoshrink).

Ora sul motivo per cui la dimensione intrinseca (altezza) del marchio non si aggiorna come la larghezza fa quando è impostata sulla sua dimensione naturale tramite "Dimensione per adattarsi ai contenuti" è oltre me. Sembra che dovrebbe assolutamente, ma non è così.

Quindi sono rimasto alla ricerca di soluzioni alternative. Per quanto posso vedere, potrebbe essere necessario impostare un limite di altezza sull'etichetta e regolare la costante di altezza dopo aver calcolato l'altezza del testo. Qualcuno ha una buona soluzione?

+0

@ hans-sjunnesson: hai provato il ritiro automatico con una regolazione della linea di base di "Nessuno"? –

+0

@ JörnEyrich mentre questo farà aderire il testo alla parte superiore del fotogramma, il frame effettivo di UILabel non si restringerà verso l'alto. Ciò significa che se si dispone, ad esempio, di un'etichetta secondaria sotto di essa, la sottoetichetta non seguirà l'etichetta verso l'alto mentre si riduce. –

+0

Controlla http://stackoverflow.com/a/4942766/1039901 –

risposta

7

Questo problema è un vero PITA da risolvere. Non aiuta che le API che funzionano siano obsolete in iOS7, o che le API di sostituzione di iOS7 siano rotte. Blah!

tua soluzione è bello, tuttavia si utilizza un API deprecato (sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode:), e non è molto ben sigillata - è necessario copiare il codice in giro a tutte le cellule o le viste in cui si desidera questo comportamento. Il lato positivo è abbastanza efficiente! Un bug potrebbe essere che l'etichetta non è stata ancora disposta quando si esegue il calcolo, ma si esegue il calcolo in base alla sua larghezza.

Propongo di incapsulare questo comportamento in una sottoclasse UILabel.Inserendo il calcolo del dimensionamento in un metodo sovrascritto intrinsicContentSize, l'etichetta si ridimensionerà automaticamente. Ho scritto quanto segue, che incorpora il codice che verrà eseguito su iOS6, e la mia versione utilizzando non deprecate API per iOS7 o meglio:

@implementation TSAutoHeightLabel 

- (CGSize) intrinsicContentSize 
{ 
    NSAssert(self.baselineAdjustment == UIBaselineAdjustmentAlignCenters, @"Please ensure you are using UIBaselineAdjustmentAlignCenters!"); 

    NSAssert(self.numberOfLines == 1, @"This is only for single-line labels!"); 

    CGSize intrinsicContentSize; 

    if ([self.text respondsToSelector: @selector(boundingRectWithSize:options:attributes:context:)]) 
    { 
     NSStringDrawingContext* context = [NSStringDrawingContext new]; 
     context.minimumScaleFactor = self.minimumScaleFactor; 

     CGSize inaccurateSize = [self.text boundingRectWithSize: CGSizeMake(self.bounds.size.width, CGFLOAT_MAX) 
                 options: NSStringDrawingUsesLineFragmentOrigin 
                attributes: @{ NSFontAttributeName : self.font } 
                 context: context].size; 

     CGSize accurateSize = [self.text sizeWithAttributes: @{ NSFontAttributeName : [UIFont fontWithName: self.font.fontName size: 12.0] } ]; 

     CGFloat accurateHeight = accurateSize.height * inaccurateSize.width/accurateSize.width; 

     intrinsicContentSize = CGSizeMake(inaccurateSize.width, accurateHeight); 
    } 
    else 
    { 
     CGFloat actualFontSize; 

#pragma GCC diagnostic push 
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" 

     [self.text sizeWithFont: self.font 
        minFontSize: self.minimumFontSize 
       actualFontSize: &actualFontSize 
         forWidth: self.frame.size.width 
        lineBreakMode: NSLineBreakByTruncatingTail]; 

#pragma GCC diagnostic pop 

     CGRect lineBox = CTFontGetBoundingBox((__bridge CTFontRef)([UIFont fontWithName: self.font.fontName size: actualFontSize])); 

     intrinsicContentSize = lineBox.size; 
    } 

    return intrinsicContentSize; 
} 

@end 

Questa implementazione non è perfetto. Dovevo assicurarmi di usare baselineAdjustment == UIBaselineAdjustmentAlignCenters, e non sono sicuro al 100% di aver capito perché. E non sono contento dei cerchi che ho dovuto attraversare per ottenere un'altezza del testo precisa. C'è anche una piccola differenza di pixel tra ciò che produce il mio calcolo e il tuo. Sentitevi liberi di giocare con esso e regolare se necessario :)

L'API boundingRectWithSize:options:attributes:context sembra piuttosto rotta per me. Mentre (soprattutto!) Vincola correttamente il testo alla dimensione di input, non calcola l'altezza corretta! L'altezza restituita si basa sull'altezza della linea del font fornito, anche se è in gioco un ridimensionamento. La mia ipotesi è questa è la ragione per cui UILabel non ha questo comportamento di default? La mia soluzione è calcolare una dimensione non vincolata in cui sia l'altezza sia la larghezza siano accurate, quindi utilizzare il rapporto tra le larghezze vincolate e non vincolate per calcolare l'altezza esatta per le dimensioni vincolate. Che PITA. Ci sono molte lamentele nei forum di sviluppo di Apple e qui su SO che sottolinea che questa API ha una serie di problemi come questo.

+0

Mi sento un marsicano per chiedere ma cos'è la PITA? – Kuzgun

+1

Dolore nella ... Aspetta, i marziani hanno asini? –

+0

Hai il tizio Tom. Anche se potrebbe sembrare un trucco, l'implementazione fa parte del calcolo della dimensione intrinseca dell'etichetta. –

-3

Quello che stai cercando sono queste due righe di codice.

myLabel.numberOfLines = 0; 
myLabel.lineBreakMode = UILineBreakModeWordWrap; 

e lo troverete anche nell'Inspector degli attributi in "Line Breaks" e "Lines".

+0

Non se non vuoi che si completi. –

2

Quindi ho trovato una soluzione alternativa. È un po 'rischioso, ma funziona.

Quindi quello che ho fatto è stato aggiungere un limite di altezza alla mia riga di testo in IB, e prendere un riferimento a quello nella mia vista.

Poi nel layoutSubviews, aggiorno la mia altezza vincolo in base alla dimensione del font, che devo calcolare:

- (void)layoutSubviews { 
    if (self.titleLabel.text) { 
     CGFloat actualFontSize; 
     CGSize titleSize = [self.titleLabel.text sizeWithFont:self.titleLabel.font minFontSize:9.0 actualFontSize:&actualFontSize forWidth:self.titleLabel.frame.size.width lineBreakMode:NSLineBreakByTruncatingTail]; 
     CGRect lineBox = CTFontGetBoundingBox((__bridge CTFontRef)([UIFont fontWithName:@"ProximaNova-Regular" size:actualFontSize])); 
     self.titleHeightConstraint.constant = lineBox.size.height; 
    } 
    [super layoutSubviews]; 
} 

All'inizio ero solo impostandolo sulle dimensioni dei caratteri, ma anche con una regolazione (* 1.2) stava ancora tagliando le dimensioni dei caratteri più piccoli. Il tasto utilizzava CTFontGetBoundingBox con la dimensione del carattere determinata dal mio calcolo.

Questo è piuttosto spiacevole, e spero che ci sia un modo migliore. Forse dovrei passare al confezionamento.

1

TomSwift grazie per la tua risposta, ho davvero faticato con questo problema.

Se qualcuno sta ancora ricevendo un comportamento strano, ho dovuto cambiare:

intrinsicContentSize = CGSizeMake (inaccurateSize.width, accurateHeight);

a

intrinsicContentSize = CGSizeMake (inaccurateSize.width, accurateHeight * 2);

quindi ha funzionato come fascino.

Problemi correlati