Autoshrink on UILabel with multiple lines

Can I use the autoshrink property with multiple lines on a UILabel ? for example, a large text size on 2 available lines is possible.

+49
ios uilabel ios5
Jan 30 '12 at 5:12
source share
16 answers

These people found a solution:

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

Their solution is as follows:

 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 font to the newly adjusted font. msg.font = font; // Put the text into the UILabel outlet variable. msg.text = self.ourText; 

To get this working, an IBOutlet must be assigned in the interface builder for UILabel.

"IBOutlet UILabel * msg;"

All the merit of the people at 11pixel.

+38
Jan 30 2018-12-12T00:
source share

I slightly modified the above code to make it a category on UILabel :

Header file:

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

And the implementation file:

 @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 
+46
Jul 27 '12 at 19:49
source share

I found this link http://beckyhansmeyer.com/2015/04/09/autoshrinking-text-in-a-multiline-uilabel/

The problem can be solved using Interface Builder in three simple steps:

  • Set AutoPlay to Minimum Font Size.
  • Set the font to your largest desired font size (20) and set Lines to, say, 10, which in my case there were as many lines that would match the label with this font size.
  • Then change the “Line Breaks” from “Word Wrap” to “Truncate Tail.”

Hope this helps!

+28
Jul 01 '15 at 22:38
source share

Here the category solution is updated to iOS 7 based on itecedor updates for iOS 6.

Header file:

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

And the implementation file:

 @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 
+24
Dec 11 '13 at 18:53
source share

The answer, designated as a solution, is hacked and inaccurate. UILabel will process it automatically if you set the following properties correctly:

numberOfLines must be nonzero

adjustsFontSizeToFitWidth should be YES

lineBreakMode must not be NSLineBreakByCharWrapping or NSLineBreakByWordWrapping

+8
Apr 27
source share

A small version adapted from @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; } } } } 
+7
Mar 18 '15 at 21:53
source share

I can not comment on the MontiRabbit post due to lack of reputation, so I will make a new answer. The solution proposed by him (and her referrer) does not work on Xcode 7.3 or higher, this is inaccurate. To make it work, in the storyboard I had to:

  • Set a width limit (net width or tail and output)
  • INSTALL AN ALTITUDE DESIGN (this is very important, usually the label height is not set with autoresist)
  • Sets the Autoshrink property to Minimum Font Scale or Minimum Font Size (works in both cases)
  • Set Line Breaks to Truncate Tail
  • Set the Lines property to a nonzero value

Hope this helps !;)

+7
Jul 21 '16 at 9:07
source share

I liked DaGaMs answer, but when using labels similar to UITableViewCells that could be returned from dequeueReusableCell: the regular font size will continue to decrease even if the original font size is still desirable for some tableView elements that have less text and can use The original size of the original label.

So, I start with the DaGaMs category as a jumping point, I created a separate class, not a separate category, and I make sure that my UILabels in my storyboard use this new class:

 #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 
+6
Aug 03 '12 at 2:20
source share

The answer itedcedor has the problem that the participant pointed out. In addition, there is no need to trim spaces. Here is the modified version:

 - (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]; } 
+6
Mar 18 '13 at 18:19
source share

Thanks to DaGaMs for this solution.

I updated it as follows:

1 - Work with iOS 6 (since both versions of MinimumFontSize and UILineBreakModeWordWrap are outdated) 2 - To remove spaces from the label text, as this will cause the resizing to fail (you do not want to know how long it took me to find this error)

 -(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]; } 
+4
Feb 14 '13 at 22:18
source share

I wrote a small category in UILabel based on the answer of "The Dude" above to achieve this functionality.

https://gist.github.com/ayushn21/d87b835b2efc756e859f

+1
Jul 17 '15 at 15:30
source share

For UIButton, these lines work for me:

 self.centerBtn.titleLabel.numberOfLines = 2; self.centerBtn.titleLabel.textAlignment = NSTextAlignmentCenter; self.centerBtn.titleLabel.adjustsFontSizeToFitWidth = YES; 
+1
Dec 11 '15 at 15:51
source share

There is an NSString method, -sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode: which, apparently, exists with iOS 2.0, but, unfortunately, it is not recommended in iOS 7 without the proposed alternative, since automatic font size reduction is not recommended . I really don’t understand Apple’s position on this, since they use it in the key record, etc., And I think that if the font sizes are within a small range, everything is in order. Here is an implementation in Swift using this method.

 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) 

I do not know how this can be achieved without using obsolete methods.

0
Jul 07 '14 at 11:34
source share

I used @wbarksdale Swift 3's solution, but found that long words are truncated in the middle. To keep the words intact, I had to change as shown:

 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
Apr 18 '17 at 20:00
source share

Try the following:

Any subclass of UILabel or the callFontSize call method after setting the text property on the label

 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
Jun 28 '17 at 9:46 on
source share
 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 } 

}

you need to assign a value to the numberOfLines property for UILabel.

0
Dec 09 '17 at 16:16
source share



All Articles