UILabel Result Strings with UILineBreakModeWordWrap

I have a UILabel whose size is calculated using the sizeWithFont: method. Line break mode is set to UILineBreakModeWordWrap (the same flag is used when calculating size using sizeWithFont: ...

Everything works fine, the shortcut is the right size and displays the text as needed.

Now I need to know the lines that are used to display the label (or the lines that are generated when using sizeWithFont: . I could technically write my own line break implementation based on space / carriage returns, but then it will not be guaranteed the same as the Apple implementation, and therefore the resulting lines will not be the ones used to calculate the text size, do not forget how to reinvent the wheel.

Ideally, I would pass my line, set the line width and line break mode, and get an array of lines representing the visual lines of the text.

Any ideas how to do this in the most elegant way?

+18
iphone cocoa-touch uilabel nsstring
Nov 02 '10 at
source share
5 answers

I do not think there is a silver bullet for this.

Here is a category method that seems to work for a few basic tests that I threw at it. There is no guarantee that it will not break with something complicated!

How it works, you need to go through string testing to find out if the word range matches the width of the label. When it calculates that the current range is too wide, it writes the range of the last match as a string.

I am not saying this is effective. A better way might be to implement your own 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 

use the following:

 NSArray* lines = [_theLabel lines]; int count = [lines count]; 
+24
Nov 09 '10 at 16:43
source share

To calculate the number of lines that a UILabel has after wrapping the text, you will need to find the leading (line height) of your UILabel font ( label.font.leading ), and then divide the height of your multi-line UILabel by the height of each line to get the number lines.

Here is an example:

 - (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 text to wrap and wrap and wrap around. Some really really long string that will cause the label 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]; } 

Or you can do it in two lines:

 [label sizeToFit]; int numLines = (int)(label.frame.size.height/label.font.leading); 
+38
Nov 13 '10 at 9:27 a.m.
source share

Just call below and pass in either UILabel or 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; } 

Now call this method: -

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

UPDATED: SWIFT CODE

 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 } 

Now call this method: -

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

Note: leading - use lineHeight. does not return the actual manual. will be formally obsolete in the future.

+11
Jan 18 '13 at 14:43
source share

for Xcode 7 and later TheTiger answer requires an update commented out by the following code:

 -(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; } 

this will only work if you use the same font and size, this is not a smart move, but it helped me, and I wanted to share it as with the current solution that I know.

0
May 19 '16 at 10:03
source share

Updated for Swift 3

To calculate the number of lines that a UILabel has, after wrapping the text, you need to divide the height of your multi-line UILabel by the height of each line ( ascender ).

When Adolfo tried to answer for some reason, label.font.leading returns 0.0, so I used label.font.ascender , which returns the height from the baseline to the top of the UILabel frame. See the figure below.

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) 
0
Jan 18 '17 at 21:54 on
source share



All Articles