2012-01-30 16 views
49

E 'possibile utilizzare la proprietà compattazione automatica in collaborazione su più righe su un UILabel? ad esempio, la dimensione del testo grande è possibile su 2 linee disponibili.Autoshrink su un UILabel con più linee

risposta

39

Queste persone pensano che una soluzione:

http://www.11pixel.com/blog/28/resize-multi-line-text-to-fit-uilabel-on-iphone/

La loro soluzione è la seguente:

int maxDesiredFontSize = 28; 
int minFontSize = 10; 
CGFloat labelWidth = 260.0f; 
CGFloat labelRequiredHeight = 180.0f; 
//Create a string with the text we want to display. 
self.ourText = @"This is your variable-length string. Assign it any way you want!"; 

/* This is where we define the ideal font that the Label wants to use. 
    Use the font you want to use and the largest font size you want to use. */ 
UIFont *font = [UIFont fontWithName:@"Marker Felt" size:maxDesiredFontSize]; 

int i; 
/* Time to calculate the needed font size. 
    This for loop starts at the largest font size, and decreases by two point sizes (i=i-2) 
    Until it either hits a size that will fit or hits the minimum size we want to allow (i > 10) */ 
for(i = maxDesiredFontSize; i > minFontSize; i=i-2) 
{ 
    // Set the new font size. 
    font = [font fontWithSize:i]; 
    // You can log the size you're trying: NSLog(@"Trying size: %u", i); 

    /* This step is important: We make a constraint box 
     using only the fixed WIDTH of the UILabel. The height will 
     be checked later. */ 
    CGSize constraintSize = CGSizeMake(labelWidth, MAXFLOAT); 

    // This step checks how tall the label would be with the desired font. 
    CGSize labelSize = [self.ourText sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap]; 

    /* Here is where you use the height requirement! 
     Set the value in the if statement to the height of your UILabel 
     If the label fits into your required height, it will break the loop 
     and use that font size. */ 
    if(labelSize.height <= labelRequiredHeight) 
     break; 
} 
// You can see what size the function is using by outputting: NSLog(@"Best size is: %u", i); 

// Set the UILabel's font to the newly adjusted font. 
msg.font = font; 

// Put the text into the UILabel outlet variable. 
msg.text = self.ourText; 

Al fine di ottenere questo lavoro, un IBOutlet deve essere assegnato nel generatore di interfaccia al UILabel.

"IBOutlet UILabel * msg;"

Tutto il merito è delle persone al 11pixel.

+0

Non funziona per me. –

46

ho modificato il codice di cui sopra un po 'per renderlo una categoria a UILabel: file di

Intestazione:

#import <UIKit/UIKit.h> 
@interface UILabel (MultiLineAutoSize) 
    - (void)adjustFontSizeToFit; 
@end 

E il file di implementazione:

@implementation UILabel (MultiLineAutoSize) 

- (void)adjustFontSizeToFit 
{ 
    UIFont *font = self.font; 
    CGSize size = self.frame.size; 

    for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumFontSize; maxSize -= 1.f) 
    { 
     font = [font fontWithSize:maxSize]; 
     CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT); 
     CGSize labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap]; 
     if(labelSize.height <= size.height) 
     { 
      self.font = font; 
      [self setNeedsLayout]; 
      break; 
     } 
    } 
    // set the font to the minimum size anyway 
    self.font = font; 
    [self setNeedsLayout]; 
} 

@end 
+0

Grande, ho solo inciampato su questo problema me stesso e questo risolve in poco tempo :-) soli due note: ** (1) ** prime due righe in interiore 'if' clausola sono ridondanti, in quanto saranno eseguiti proprio sotto il ciclo 'for'. ** (2) ** se c'è anche bisogno che il testo di UILabel sia allineato verticalmente verso l'alto (nel caso utilizzi meno righe di quelle che possono essere effettivamente mostrate nell'etichetta), è sufficiente impostare l'altezza dell'etichetta su labelSize.height. Si noti che sizeToFit non può essere utilizzato per questo scopo, in quanto rompe il testo formattato in precedenza e lo ridimensiona alla dimensione del carattere originale. – FurloSK

+5

Questo è perfetto, ma minimumFontSize è deprecato in iOS6 - come potrebbe essere modificato invece per funzionare con minimumScaleFactor? – bmueller

+0

Proprio quello di cui avevo bisogno. Grazie. +1 –

6

mi è piaciuto risposta di DaGaMs, ma utilizzando etichette come UITableViewCells che potrebbero essere restituite da dequeueReusableCell :, le dimensioni regolari dei caratteri continuerebbero a ridursi anche se la dimensione del carattere originale era ancora desiderato per alcune celle tableView che avevano meno testo e potevano sfruttare la dimensione originale del font dell'etichetta originale.

così, ho iniziato con la categoria di DaGaMs come punto di partenza di tutto, ho creato una classe separata, piuttosto che una categoria a parte, e mi assicuro che i miei UILabels nel mio storyboard fare uso di questa nuova classe:

#import "MultiLineAutoShrinkLabel.h" 

@interface MultiLineAutoShrinkLabel() 
@property (readonly, nonatomic) UIFont* originalFont; 
@end 

@implementation MultiLineAutoShrinkLabel 

@synthesize originalFont = _originalFont; 

- (UIFont*)originalFont { return _originalFont ? _originalFont : (_originalFont = self.font); } 

- (void)quoteAutoshrinkUnquote 
{ 
    UIFont* font = self.originalFont; 
    CGSize frameSize = self.frame.size; 

    CGFloat testFontSize = _originalFont.pointSize; 
    for (; testFontSize >= self.minimumFontSize; testFontSize -= 0.5) 
    { 
     CGSize constraintSize = CGSizeMake(frameSize.width, MAXFLOAT); 
     CGSize testFrameSize = [self.text sizeWithFont:(font = [font fontWithSize:testFontSize]) 
            constrainedToSize:constraintSize 
             lineBreakMode:self.lineBreakMode]; 
     // the ratio of testFontSize to original font-size sort of accounts for number of lines 
     if (testFrameSize.height <= frameSize.height * (testFontSize/_originalFont.pointSize)) 
      break; 
    } 

    self.font = font; 
    [self setNeedsLayout]; 
} 

@end 
+1

+1 per gestire UITableViewCells. Ma dov'è il codice per la tua interfaccia MultilineAutoShrinkLabel.h? – Rhubarb

+0

@Rhubarb, è solo banale: sottoclasse di UILabel con una funzione, '- (void) quoteAutoshrinkUnquote;' ... e in realtà, ho passato a usare qualcosa di simile, ma un po 'più complicato che si occupa della accessoryView sul lato destro e il fatto che può essere di 2 dimensioni diverse, consentendo di avere più o meno spazio sullo schermo per cella. –

+0

Ho usato questa risposta per rispondere alla mia qeustion e anche aggiornato il codice per rimuovere i metodi deprecati http://stackoverflow.com/a/30898604/758083 – Hackmodford

4

Grazie a DaGaMs per questa soluzione.

Ho aggiornato come segue:

1 - Per lavorare con iOS 6 (dal momento che entrambi minimumFontSize e UILineBreakModeWordWrap sono deprecati) 2 - Per Rimuove gli spazi dal testo dell'etichetta, in quanto causerà il ridimensionamento a FAIL (non si vuole sapere quanto tempo mi ha portato a scoprire che bug) risposta

-(void)adjustFontSizeToFit 
{ 
    self.text = [self.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 

    UIFont *font = self.font; 
    CGSize size = self.frame.size; 

    for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumScaleFactor; maxSize -= 1.f) 
    { 
     font = [font fontWithSize:maxSize]; 
     CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT); 
     CGSize labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping]; 
     if(labelSize.height <= size.height) 
     { 
      self.font = font; 
      [self setNeedsLayout]; 
      break; 
     } 
    } 
    // set the font to the minimum size anyway 
    self.font = font; 
    [self setNeedsLayout]; 
} 
+2

'minimumScaleFactor' è un valore decimale, come 0.2. 'maxSize> = self.minimumScaleFactor' ridurrebbe quindi la dimensione a 0,2. Invece, non vuoi 'maxSize> = self.minimumScaleFactor * self.font.pointSize' o qualcosa di simile? – pwightman

6

di itedcedor ha un problema che pwightman sottolineato. Inoltre, non è necessario tagliare gli spazi bianchi. Qui è la versione modificata:

- (void)adjustFontSizeToFit { 
    UIFont *font = self.font; 
    CGSize size = self.frame.size; 

    for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumScaleFactor * self.font.pointSize; maxSize -= 1.f) { 
     font = [font fontWithSize:maxSize]; 
     CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT); 
     CGSize labelSize = [self.text sizeWithFont:font constrainedToSize:constraintSize lineBreakMode:NSLineBreakByWordWrapping]; 

     if(labelSize.height <= size.height) { 
      self.font = font; 
      [self setNeedsLayout]; 
      break; 
     } 
    } 

    // set the font to the minimum size anyway 
    self.font = font; 
    [self setNeedsLayout]; 
} 
+0

Il problema che sto affrontando utilizzando questo è che il testo sembra essere allineato a sinistra, non importa quello che fai. Aggiornamento: Sembra che deselezionare "Stringa distanziamento lettera" risolve il problema. – Pierre

24

Ecco la soluzione categoria aggiornato a iOS 7 in base al largo di aggiornamenti di itecedor per iOS 6.

file di intestazione:

#import <UIKit/UIKit.h> 
@interface UILabel (MultiLineAutoSize) 
    - (void)adjustFontSizeToFit; 
@end 

E il file di implementazione:

@implementation UILabel (MultiLineAutoSize) 


- (void)adjustFontSizeToFit { 
    UIFont *font = self.font; 
    CGSize size = self.frame.size; 

    for (CGFloat maxSize = self.font.pointSize; maxSize >= self.minimumScaleFactor * self.font.pointSize; maxSize -= 1.f) 
    { 
     font = [font fontWithSize:maxSize]; 
     CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT); 

     CGRect textRect = [self.text boundingRectWithSize:constraintSize 
              options:NSStringDrawingUsesLineFragmentOrigin 
              attributes:@{NSFontAttributeName:font} 
              context:nil]; 

     CGSize labelSize = textRect.size; 


     if(labelSize.height <= size.height) 
     { 
      self.font = font; 
      [self setNeedsLayout]; 
      break; 
     } 
    } 
    // set the font to the minimum size anyway 
    self.font = font; 
    [self setNeedsLayout]; } 


@end 
+2

Non è necessario applicare il valore minimoScaleFactor alla pointSize iniziale? (ad esempio se il fattore è 0.5, saresti in grado di ottenere font molto piccoli qui). – iceydee

+0

Hai ragione! Modificherò la risposta per riflettere questo. – stevenpaulr

+1

Funziona come un incantesimo, non dimenticare di impostare adjustFontSizeToFitWidth su YES per UILabel –

0

C'è un metodo su NSString, -sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode: che a quanto pare esiste dal iOS 2.0, ma purtroppo è deprecato in iOS 7 senza un'alternativa suggerita in quanto la riduzione automatica delle dimensioni dei caratteri è scoraggiata. Non capisco bene la posizione di Apple su questo come lo usano in keynote ecc e penso che se le dimensioni del carattere sono all'interno di un piccolo intervallo è ok. Ecco un'implementazione in Swift usando questo metodo.

var newFontSize: CGFloat = 30 
    let font = UIFont.systemFontOfSize(newFontSize) 
    (self.label.text as NSString).sizeWithFont(font, minFontSize: 20, actualFontSize: &newFontSize, forWidth: self.label.frame.size.width, lineBreakMode: NSLineBreakMode.ByWordWrapping) 
    self.label.font = font.fontWithSize(newFontSize) 

Non sono a conoscenza di un modo in cui questo può essere raggiunto senza utilizzare metodi deprecati.

7

Una versione swifty adattata da @DaGaMs.

SWIFT 2:

extension UILabel { 
    func adjustFontSizeToFit(minimumFontSize: CGFloat, maximumFontSize: CGFloat? = nil) { 
     let maxFontSize = maximumFontSize ?? font.pointSize 
     for size in stride(from: maxFontSize, to: minimumFontSize, by: -CGFloat(0.1)) { 
      let proposedFont = font.fontWithSize(size) 
      let constraintSize = CGSizeMake(bounds.size.width, CGFloat(MAXFLOAT)) 
      let labelSize = ((text ?? "") as NSString).boundingRectWithSize(constraintSize, 
       options: .UsesLineFragmentOrigin, 
       attributes: [NSFontAttributeName: proposedFont], 
       context: nil) 
      if labelSize.height <= bounds.size.height { 
       font = proposedFont 
       setNeedsLayout() 
       break; 
      } 
     } 
    } 
} 

SWIFT 3:

extension UILabel { 
    func adjustFontSizeToFit(minimumFontSize: CGFloat, maximumFontSize: CGFloat? = nil) { 
     let maxFontSize = maximumFontSize ?? font.pointSize 
     for size in stride(from: maxFontSize, to: minimumFontSize, by: -CGFloat(0.1)) { 
      let proposedFont = font.withSize(size) 
      let constraintSize = CGSize(width: bounds.size.width, height: CGFloat(MAXFLOAT)) 
      let labelSize = ((text ?? "") as NSString).boundingRect(with: constraintSize, 
                      options: .usesLineFragmentOrigin, 
                      attributes: [NSFontAttributeName: proposedFont], 
                      context: nil) 
      if labelSize.height <= bounds.size.height { 
       font = proposedFont 
       setNeedsLayout() 
       break; 
      } 
     } 
    } 
} 
+0

Non funziona affatto –

8

La risposta contrassegnata come la soluzione è hacky e imprecisa. UILabel tratteremo automaticamente se si imposta le seguenti proprietà in modo corretto:

numberOfLines deve essere diverso da zero

adjustsFontSizeToFitWidth deve essere YES

lineBreakMode must non essere NSLineBreakByCharWrapping o NSLineBreakByWordWrapping

+1

Ho avuto successo utilizzando: numberOfLines = 0, lineBreakMode = NSLineBreakByTruncatingTail (o NSLineBreakByTruncatingHead o NSLineBreakByTruncatingMiddle) e con un vincolo di altezza sull'etichetta. –

+0

Questa dovrebbe essere la risposta accettata. –

29

ho trovato questo link http://beckyhansmeyer.com/2015/04/09/autoshrinking-text-in-a-multiline-uilabel/

Il problema può essere risolto utilizzando l'Interface Builder in 3 semplici passi: “Dimensione minima del carattere”

  1. Set “compattazione automatica” per
  2. impostare il carattere al vostro più grande dimensione dei caratteri desiderabili (20) e impostare Linee per, ad esempio, 10, che nel mio caso corrispondevano a molte righe che si sarebbero adattate all'etichetta con quella dimensione di carattere.
  3. Poi, il cambiamento “Linea Breaks” da “A capo automatico” a “troncano Tail”.

Speranza che aiuta!

+0

Seleziono Truncate Tail e le mie linee invisibili sono ora visibili. – EFE

+1

Molto più soluzione migliore del codice personalizzato. Buon lavoro. – Kris

+0

Grazie per aver segnalato "Truncate Tail", questo è ciò che fa la differenza. –

2

Per UIButton, solo queste linee stanno lavorando per me:

self.centerBtn.titleLabel.numberOfLines = 2; 
self.centerBtn.titleLabel.textAlignment = NSTextAlignmentCenter; 
self.centerBtn.titleLabel.adjustsFontSizeToFitWidth = YES; 
7

non posso commentare il posto di MontiRabbit a causa di reputazione manca, quindi farò una nuova risposta.La soluzione che lui (e il suo referente) ha proposto non funziona su Xcode 7.3 o meglio, è imprecisa. Per farlo funzionare, in storyboard, ho dovuto:

  1. Impostare un vincolo di larghezza (larghezza pura o la coda & piombo)
  2. SET un vincolo ALTEZZA (questo è molto importante, di solito con AutoResize uno non lo fa impostare l'altezza etichetta)
  3. Set "Autoshrink" proprietà su "scala del carattere minimo" o "la dimensione del carattere minima" (opere in entrambi i casi)
  4. Set "interruzioni di riga" proprietà "Tronca Tail"
  5. Set " Righe "proprietà su un valore diverso da zero

Speranza che aiuta! ;)

+0

L'impostazione di interruzioni di linea su "Troncare coda" è contro-intuitiva, non dovrebbe essere necessaria, ed è ciò che rende improvvisamente la magia accadere quando hai tutto il resto impostato nel modo in cui ti aspetteresti di aver bisogno. Grazie! Può confermare che il vincolo di altezza non deve essere un'altezza fissa, può essere solo collegarlo alle dimensioni del contenitore o ad una proporzione di esso. –

+0

@DuncanBabbage, prego! Quindi possiamo dire che l'altezza dell'etichetta doveva essere calcolabile dal compilatore, in qualche modo, con un vincolo di altezza o con il riferimento genitore! – Nem

0

ho usato soluzione Swift 3 @ di wbarksdale ma ho trovato che le parole lunghe venivano troncati nel mezzo. Per mantenere intatta parole, ho dovuto modificare come indicato:

extension UILabel { 
    func adjustFontSizeToFit(minimumFontSize: CGFloat, maximumFontSize: CGFloat? = nil) { 
     let maxFontSize = maximumFontSize ?? font.pointSize 
     let words = self.text?.components(separatedBy: " ") 
     var longestWord: String? 
     if let max = words?.max(by: {$1.characters.count > $0.characters.count}) { 
      longestWord = max 
     } 
     for size in stride(from: maxFontSize, to: minimumFontSize, by: -CGFloat(0.1)) { 
      let proposedFont = font.withSize(size) 
      let constraintSize = CGSize(width: bounds.size.width, height: CGFloat(MAXFLOAT)) 
      let labelSize = ((text ?? "") as NSString).boundingRect(with: constraintSize, 
                    options: .usesLineFragmentOrigin, 
                    attributes: [NSFontAttributeName: proposedFont], 
                    context: nil) 

      let wordConstraintSize = CGSize(width: CGFloat(MAXFLOAT), height: CGFloat(MAXFLOAT)) 
      let longestWordSize = ((longestWord ?? "") as NSString).boundingRect(with: wordConstraintSize, 
                    options: .usesFontLeading, 
                    attributes: [NSFontAttributeName: proposedFont], 
                    context: nil) 

      if labelSize.height <= bounds.size.height && longestWordSize.width < constraintSize.width { 
       font = proposedFont 
       setNeedsLayout() 
       break 
      } 
     } 
    } 
} 
0

Prova questa:

Sia sottoclasse UILabel o chiamare adjustFontSize metodo dopo l'impostazione della proprietà di testo su un'etichetta

override var text : String? { didSet { self.adjustFontSize() } } 

func adjustFontSize() 
{ 
    var lineCount = self.string.components(separatedBy: "\n").count - 1 
    var textArray = self.string.components(separatedBy: " ") 
    var wordsToCompare = 1 
    while(textArray.count > 0) 
    { 
     let words = textArray.first(n: wordsToCompare).joined(separator: " ") 
     let wordsWidth = words.widthForHeight(0, font: self.font) 
     if(wordsWidth > self.frame.width) 
     { 
      textArray.removeFirst(wordsToCompare) 
      lineCount += 1 
      wordsToCompare = 1 
     } 
     else if(wordsToCompare > textArray.count) 
     { 
      break 
     } 
     else 
     { 
      wordsToCompare += 1 
     } 
    } 
    self.numberOfLines = lineCount + 1 
} 
0
extension UILabel{ 

func adjustFont(minSize:Int, maxSize:Int){ 
    var newFont = self.font 
    for index in stride(from: maxSize, to: minSize, by: -1) { 
     newFont = UIFont.systemFont(ofSize: CGFloat(index)) 
     let size = CGSize(width: self.frame.width, height: CGFloat(Int.max)) 
     let size2 = (self.text! as NSString).boundingRect(with: size, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [NSAttributedStringKey.font:newFont!], context: nil).size 
     if size2.height < self.frame.size.height{ 
      break 
     } 
    } 
    self.font = newFont 
} 

}

è necessario assegnare valore alla proprietà NumberOfLines di UILabel pure.

Problemi correlati