2010-11-02 12 views
18

Ho un UILabel le cui dimensioni sono calcolate con il metodo sizeWithFont:. La modalità interruzione linea è impostata su UILineBreakModeWordWrap (lo stesso flag viene utilizzato per calcolare la dimensione con sizeWithFont:) ...Linee risultanti di UILabel con UILineBreakModeWordWrap

Tutto funziona alla grande, l'etichetta è correttamente dimensionata e visualizza il mio testo come richiesto.

Ora ho bisogno di conoscere le linee utilizzate per visualizzare l'etichetta (o le linee che vengono generate quando viene utilizzato sizeWithFont:). Potrei tecnicamente scrivere la mia implementazione dell'interruzione di riga in base agli spazi/ritorni a capo, ma non sarà garantita allo stesso modo dell'implementazione di Apple e quindi le linee risultanti non saranno quelle che vengono utilizzate per calcolare la dimensione del testo , non importa il fatto di reinventare la ruota.

Idealmente, vorrei passare la mia stringa, specificare la larghezza e la modalità di interruzione di riga e ricevere una serie di stringhe che rappresentano le linee visive del testo.

Qualche idea su come farlo accadere nel modo più elegante?

risposta

24

Non penso ci sia alcun proiettile d'argento per questo.

Ecco un metodo di categoria che sembra funzionare per i pochi casi di test di base che ho lanciato. Non garantisce che non si rompa con qualcosa di complesso!

Il modo in cui funziona è passare attraverso il test delle stringhe per verificare se un intervallo di parole si adatta alla larghezza dell'etichetta. Quando calcola che l'intervallo corrente è troppo ampio, registra l'intervallo dell'ultimo raccordo come una linea.

Non ritengo che questo sia efficiente. Un modo migliore può solo essere quello di implementare il proprio UILabel ...

@interface UILabel (Extensions) 

- (NSArray*) lines; 

@end 

@implementation UILabel (Extensions) 

- (NSArray*) lines 
{ 
    if (self.lineBreakMode != UILineBreakModeWordWrap) 
    { 
     return nil; 
    } 

    NSMutableArray* lines = [NSMutableArray arrayWithCapacity:10]; 

    NSCharacterSet* wordSeparators = [NSCharacterSet whitespaceAndNewlineCharacterSet]; 

    NSString* currentLine = self.text; 
    int textLength = [self.text length]; 

    NSRange rCurrentLine = NSMakeRange(0, textLength); 
    NSRange rWhitespace = NSMakeRange(0,0); 
    NSRange rRemainingText = NSMakeRange(0, textLength); 
    BOOL done = NO; 
    while (!done) 
    { 
     // determine the next whitespace word separator position 
     rWhitespace.location = rWhitespace.location + rWhitespace.length; 
     rWhitespace.length = textLength - rWhitespace.location; 
     rWhitespace = [self.text rangeOfCharacterFromSet: wordSeparators options: NSCaseInsensitiveSearch range: rWhitespace]; 
     if (rWhitespace.location == NSNotFound) 
     { 
      rWhitespace.location = textLength; 
      done = YES; 
     } 

     NSRange rTest = NSMakeRange(rRemainingText.location, rWhitespace.location-rRemainingText.location); 

     NSString* textTest = [self.text substringWithRange: rTest]; 

     CGSize sizeTest = [textTest sizeWithFont: self.font forWidth: 1024.0 lineBreakMode: UILineBreakModeWordWrap]; 
     if (sizeTest.width > self.bounds.size.width) 
     { 
      [lines addObject: [currentLine stringByTrimmingCharactersInSet:wordSeparators]]; 
      rRemainingText.location = rCurrentLine.location + rCurrentLine.length; 
      rRemainingText.length = textLength-rRemainingText.location; 
      continue; 
     } 

     rCurrentLine = rTest; 
     currentLine = textTest; 
    } 

    [lines addObject: [currentLine stringByTrimmingCharactersInSet:wordSeparators]]; 

    return lines; 
} 

@end 

uso in questo modo:

NSArray* lines = [_theLabel lines]; 

int count = [lines count]; 
+1

Grazie TomSwift per il vostro contributo ... Questa sorta di opere per alcuni casi, ma sicuramente mi ha dato una grande spinta, sto lavorando su una soluzione a prova di proiettile di più in base al codice di . Grazie ancora! – Nick

+0

Quali casi lo hanno rotto? – TomSwift

+0

Bella soluzione TomSwift. Ma non funziona per me, è sempre il testo su una riga, anche se int conteggio a volte sono 9, 10 o 20. Il mio codice: \t self.labelDescription.text = event.description; \t self.labelDescription.lineBreakMode = UILineBreakModeWordWrap; \t self.labelDescription.font = [UIFont fontWithName: @ dimensione "Helvetica": 13.0]; \t \t NSArray * lines = [self.labelDescription lines]; \t \t conteggio int = [numero di righe]; \t self.labelDescription.numberOfLines = count; –

38

Per calcolare il numero di righe che un UILabel ha dopo il confezionamento è il testo è necessario per trovare l'iniziale (altezza della linea) del font UILabel (label.font.leading) e quindi dividere l'altezza della linea multipla UILabel in base all'altezza di ogni linea per ottenere il numero di linee.

Ecco un esempio:

- (void)viewDidLoad { 

    [super viewDidLoad]; 

    UILabel *label = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease]; 
    label.numberOfLines = 0; 
    label.lineBreakMode = UILineBreakModeWordWrap; 
    label.text = @"Some really really long string that will cause the label's text to wrap and wrap and wrap around. Some really really long string that will cause the label's text to wrap and wrap and wrap around."; 

    CGRect frame = label.frame; 
    frame.size.width = 150.0f; 
    frame.size = [label sizeThatFits:frame.size]; 
    label.frame = frame; 

    CGFloat lineHeight = label.font.leading; 
    NSUInteger linesInLabel = floor(frame.size.height/lineHeight); 
    NSLog(@"Number of lines in label: %i", linesInLabel); 

    [self.view addSubview:label]; 

} 

Oppure, si potrebbe fare in due linee:

[label sizeToFit]; 
int numLines = (int)(label.frame.size.height/label.font.leading); 
+0

ah, questo è molto intelligente. più semplice della mia soluzione, di sicuro! – TomSwift

+3

Si noti che ** "leading" ** è pronunciato "ledding". (Esattamente come in "Led Zepplin", NON come in "seguire il leader".) Significa semplicemente il metallo, led (cioè la sostanza atomica Pb) - la parola viene dai primi tempi della tipografia quando si mettono letteralmente strisce led tra le lettere per creare il, beh, ledding. – Fattie

+0

Grazie .... [label sizeToFit]; int numLines = (int) (label.frame.size.height/label.font.leading); ha funzionato per me –

11

Basta chiamare sotto metodo e passare sia UILabel o UITextView:

-(NSInteger)getNumberOfLinesInLabelOrTextView:(id)obj 
{ 
    NSInteger lineCount = 0; 
    if([obj isKindOfClass:[UILabel class]]) 
    { 
     UILabel *label = (UILabel *)obj; 

     // This method is deprecated in iOS 7.0 or later 
     // CGSize requiredSize = [label.text sizeWithFont:label.font constrainedToSize:label.frame.size lineBreakMode:label.lineBreakMode]; 

     CGSize requiredSize = [label.text boundingRectWithSize:CGSizeMake(CGRectGetWidth(label.frame), CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:label.font} context:nil].size; 

     int charSize = label.font.leading; 
     int rHeight = requiredSize.height; 

     lineCount = rHeight/charSize; 
    } 
    else if ([obj isKindOfClass:[UITextView class]]) 
    { 
     UITextView *textView = (UITextView *)obj; 
     lineCount = textView.contentSize.height/textView.font.leading; 
    } 

    return lineCount; 
} 

Ora chiama questo metodo: -

NSLog(@"%d",[self getNumberOfLinesInLabelOrTextView:label]); 
NSLog(@"%d",[self getNumberOfLinesInLabelOrTextView:textView]); 

AGGIORNAMENTO: CODICE SWIFT

func getNumberOfLinesInLabelOrTextView(obj:AnyObject) -> NSInteger { 

    var lineCount: NSInteger = 0 
    if (obj.isKindOfClass(UILabel)) { 

     let label: UILabel = obj as! UILabel 
     let requiredSize: CGSize = (label.text)!.boundingRectWithSize(CGSizeMake(CGRectGetWidth(label.frame), CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: label.font], context: nil).size 
     let charSize: CGFloat = label.font.leading 
     let rHeight: CGFloat = requiredSize.height 
     lineCount = (NSInteger)(rHeight/charSize) 
    } 
    else if (obj.isKindOfClass(UITextView)){ 

     let textView: UITextView = obj as! UITextView 
     lineCount = (NSInteger)(textView.contentSize.height/textView.font.leading) 
    } 

    return lineCount 
} 

Ora chiamare questo metodo: -

println("%d \(self.getNumberOfLinesInLabelOrTextView(textView))") 
println("%d \(self.getNumberOfLinesInLabelOrTextView(label))") 

Nota: leading - utilizzare lineHeight. non restituisce il vantaggio effettivo. sarà formalmente deprecato in futuro.

+0

Questo ha funzionato bene ma quando aggiorno xcode a 7 è mostrato errore "exc_arithmetic code = exc_i386_div" sulla riga lineCount = rHeight/charSize; . Così ho fatto lineCount = rHeight/1; ora sta funzionando. Se sbaglio, guidami, correggine uno :) – user3589771

0

per Xcode 7 e fino risposta di TheTiger ha bisogno di un aggiornamento commentato il codice qui sotto:

-(NSInteger)getNumberOfLinesInLabelOrTextView:(id)obj 
{ 
    NSInteger lineCount = 0; 
    if([obj isKindOfClass:[UILabel class]]) 
    { 
     UILabel *label = (UILabel *)obj; 
     CGSize requiredSize = [label.text boundingRectWithSize:CGSizeMake(CGRectGetWidth(label.frame), CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:label.font} context:nil].size; 

     int charSize = label.font.leading; 
     // now listen , you need to set the text or label with only 1 
     // then nslog(@"%d",charSize); 
     // then change the line int charSize = label.font.leading; into 
     // int charSize = the printed value in case of 1 line 
     int rHeight = requiredSize.height; 

     lineCount = rHeight/charSize; 
    } 
    else if ([obj isKindOfClass:[UITextView class]]) 
    { 
     UITextView *textView = (UITextView *)obj; 
     lineCount = textView.contentSize.height/textView.font.leading; 
    } 

    return lineCount; 
} 

questo funziona solo se si utilizza lo stesso tipo di carattere e dimensione, non è una mossa intelligente, ma mi ha aiutato e ho voluto condividere come la soluzione attuale so

0

aggiornato per Swift 3

per calcolare il numero di linee che UILabel ha, dopo il confezionamento il suo testo, è necessario di visualizzare l'altezza della UILabel multilinea all'altezza di ciascuna riga (ascender).

Quando si cerca la risposta di Adolfo, per qualche ragione, label.font.leading restituito 0.0, quindi ho usato label.font.ascender, che restituisce l'altezza dalla base all'estremità superiore del telaio s' il UILabel. Vedi l'immagine qui sotto.

enter image description here

//Makes label go to another line if necessary 
label.numberOfLines = 0 //Set num of lines to infinity 
label.lineBreakMode = .byWordWrapping 
label.sizeToFit() 
let numLines = Int(label.frame.size.height/label.font.ascender)