I currently have a UILabel
:
factLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 100, 280, 100)];
factLabel.text = @"some text some text some text some text";
factLabel.backgroundColor = [UIColor clearColor];
factLabel.lineBreakMode = UILineBreakModeWordWrap;
factLabel.numberOfLines = 10;
[self.view addSubview:factLabel];
Throughout the life of my iOS application, factLabel
gets a bunch of different values. Some with multiple sentences, others with just 5 or 6 words.
How can I s开发者_如何学JAVAet up the UILabel
so that the font size changes so that the text always fits in the bounds I defined?
Single line:
factLabel.numberOfLines = 1;
factLabel.minimumFontSize = 8;
factLabel.adjustsFontSizeToFitWidth = YES;
The above code will adjust your text's font size down to (for example) 8
trying to fit your text within the label.
numberOfLines = 1
is mandatory.
Multiple lines:
For numberOfLines > 1
there is a method to figure out the size of final text through NSString's sizeWithFont:... UIKit addition methods, for example:
CGSize lLabelSize = [yourText sizeWithFont:factLabel.font
forWidth:factLabel.frame.size.width
lineBreakMode:factLabel.lineBreakMode];
After that you can just resize your label using resulting lLabelSize
, for example (assuming that you will change only label's height):
factLabel.frame = CGRectMake(factLabel.frame.origin.x, factLabel.frame.origin.y, factLabel.frame.size.width, lLabelSize.height);
iOS6
Single line:
Starting with iOS6, minimumFontSize
has been deprecated. The line
factLabel.minimumFontSize = 8.;
can be changed to:
factLabel.minimumScaleFactor = 8./factLabel.font.pointSize;
iOS7
Multiple lines:
Starting with iOS7, sizeWithFont
becomes deprecated.
Multiline case is reduced to:
factLabel.numberOfLines = 0;
factLabel.lineBreakMode = NSLineBreakByWordWrapping;
CGSize maximumLabelSize = CGSizeMake(factLabel.frame.size.width, CGFLOAT_MAX);
CGSize expectSize = [factLabel sizeThatFits:maximumLabelSize];
factLabel.frame = CGRectMake(factLabel.frame.origin.x, factLabel.frame.origin.y, expectSize.width, expectSize.height);
iOS 13 (Swift 5):
label.adjustsFontSizeToFitWidth = true
label.minimumScaleFactor = 0.5
minimumFontSize
has been deprecated with iOS 6. You can use minimumScaleFactor
.
yourLabel.adjustsFontSizeToFitWidth=YES;
yourLabel.minimumScaleFactor=0.5;
This will take care of your font size according width of label and text.
Single line- There are two ways, you can simply change.
1- Pragmatically (Swift 3)
Just add the following code
yourLabel.numberOfLines = 1;
yourLabel.minimumScaleFactor = 0.7;
yourLabel.adjustsFontSizeToFitWidth = true;
2 - Using UILabel Attributes inspector
i- Select your label- Set number of lines 1.
ii- Autoshrink- Select Minimum Font Scale from drop down
iii- Set Minimum Font Scale value as you wish , I have set 0.7 as in below image. (default is 0.5)
Based on @Eyal Ben Dov's answer you may want to create a category to make it flexible to use within another apps of yours.
Obs.: I've updated his code to make compatible with iOS 7
-Header file
#import <UIKit/UIKit.h>
@interface UILabel (DynamicFontSize)
-(void) adjustFontSizeToFillItsContents;
@end
-Implementation file
#import "UILabel+DynamicFontSize.h"
@implementation UILabel (DynamicFontSize)
#define CATEGORY_DYNAMIC_FONT_SIZE_MAXIMUM_VALUE 35
#define CATEGORY_DYNAMIC_FONT_SIZE_MINIMUM_VALUE 3
-(void) adjustFontSizeToFillItsContents
{
NSString* text = self.text;
for (int i = CATEGORY_DYNAMIC_FONT_SIZE_MAXIMUM_VALUE; i>CATEGORY_DYNAMIC_FONT_SIZE_MINIMUM_VALUE; i--) {
UIFont *font = [UIFont fontWithName:self.font.fontName size:(CGFloat)i];
NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName: font}];
CGRect rectSize = [attributedText boundingRectWithSize:CGSizeMake(self.frame.size.width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin context:nil];
if (rectSize.size.height <= self.frame.size.height) {
self.font = [UIFont fontWithName:self.font.fontName size:(CGFloat)i];
break;
}
}
}
@end
-Usage
#import "UILabel+DynamicFontSize.h"
[myUILabel adjustFontSizeToFillItsContents];
Cheers
It's 2015. I had to go to find a blog post that would explain how to do it for the latest version of iOS and XCode with Swift so that it would work with multiple lines.
- set “Autoshrink” to “Minimum font size.”
- set the font to the largest desirable font size (I chose 20)
- Change “Line Breaks” from “Word Wrap” to “Truncate Tail.”
Source: http://beckyhansmeyer.com/2015/04/09/autoshrinking-text-in-a-multiline-uilabel/
Swift version:
textLabel.adjustsFontSizeToFitWidth = true
textLabel.minimumScaleFactor = 0.5
Here's a Swift extension for UILabel. It runs a binary search algorithm to resize the font based off the width and height of the label's bounds. Tested to work with iOS 9 and autolayout.
USAGE: Where <label>
is your pre-defined UILabel that needs font resizing
<label>.fitFontForSize()
By Default, this function searches in within the range of 5pt and 300pt font sizes and sets the font to fit its text "perfectly" within the bounds (accurate within 1.0pt). You could define the parameters so that it, for example, searches between 1pt and the label's current font size accurately within 0.1pts in the following way:
<label>.fitFontForSize(1.0, maxFontSize: <label>.font.pointSize, accuracy:0.1)
Copy/Paste the following code into your file
extension UILabel {
func fitFontForSize(var minFontSize : CGFloat = 5.0, var maxFontSize : CGFloat = 300.0, accuracy : CGFloat = 1.0) {
assert(maxFontSize > minFontSize)
layoutIfNeeded() // Can be removed at your own discretion
let constrainedSize = bounds.size
while maxFontSize - minFontSize > accuracy {
let midFontSize : CGFloat = ((minFontSize + maxFontSize) / 2)
font = font.fontWithSize(midFontSize)
sizeToFit()
let checkSize : CGSize = bounds.size
if checkSize.height < constrainedSize.height && checkSize.width < constrainedSize.width {
minFontSize = midFontSize
} else {
maxFontSize = midFontSize
}
}
font = font.fontWithSize(minFontSize)
sizeToFit()
layoutIfNeeded() // Can be removed at your own discretion
}
}
NOTE: Each of the layoutIfNeeded()
calls can be removed at your own discretion
Its a little bit not sophisticated but this should work, for example lets say you want to cap your uilabel to 120x120, with max font size of 28:
magicLabel.numberOfLines = 0;
magicLabel.lineBreakMode = NSLineBreakByWordWrapping;
...
magicLabel.text = text;
for (int i = 28; i>3; i--) {
CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:(CGFloat)i] constrainedToSize:CGSizeMake(120.0f, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
if (size.height < 120) {
magicLabel.font = [UIFont systemFontOfSize:(CGFloat)i];
break;
}
}
Just send the sizeToFit message to the UITextView. It will adjust its own height to just fit its text. It will not change its own width or origin.
[textViewA1 sizeToFit];
Swift 2.0 Version:
private func adapteSizeLabel(label: UILabel, sizeMax: CGFloat) {
label.numberOfLines = 0
label.lineBreakMode = NSLineBreakMode.ByWordWrapping
let maximumLabelSize = CGSizeMake(label.frame.size.width, sizeMax);
let expectSize = label.sizeThatFits(maximumLabelSize)
label.frame = CGRectMake(label.frame.origin.x, label.frame.origin.y, expectSize.width, expectSize.height)
}
This solution works for multiline:
After following several articles, and requiring a function that would automatically scale the text and adjust the line count to best fit within the given label size, I wrote a function myself. (ie. a short string would fit nicely on one line and use a large amount of the label frame, whereas a long strong would automatically split onto 2 or 3 lines and adjust the size accordingly)
Feel free to re-use it and tweak as required. Make sure you call it after viewDidLayoutSubviews
has finished so that the initial label frame has been set.
+ (void)setFontForLabel:(UILabel *)label withMaximumFontSize:(float)maxFontSize andMaximumLines:(int)maxLines {
int numLines = 1;
float fontSize = maxFontSize;
CGSize textSize; // The size of the text
CGSize frameSize; // The size of the frame of the label
CGSize unrestrictedFrameSize; // The size the text would be if it were not restricted by the label height
CGRect originalLabelFrame = label.frame;
frameSize = label.frame.size;
textSize = [label.text sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize: fontSize]}];
// Work out the number of lines that will need to fit the text in snug
while (((textSize.width / numLines) / (textSize.height * numLines) > frameSize.width / frameSize.height) && (numLines < maxLines)) {
numLines++;
}
label.numberOfLines = numLines;
// Get the current text size
label.font = [UIFont systemFontOfSize:fontSize];
textSize = [label.text boundingRectWithSize:CGSizeMake(frameSize.width, CGFLOAT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
attributes:@{NSFontAttributeName : label.font}
context:nil].size;
// Adjust the frame size so that it can fit text on more lines
// so that we do not end up with truncated text
label.frame = CGRectMake(label.frame.origin.x, label.frame.origin.y, label.frame.size.width, label.frame.size.width);
// Get the size of the text as it would fit into the extended label size
unrestrictedFrameSize = [label textRectForBounds:CGRectMake(0, 0, label.bounds.size.width, CGFLOAT_MAX) limitedToNumberOfLines:numLines].size;
// Keep reducing the font size until it fits
while (textSize.width > unrestrictedFrameSize.width || textSize.height > frameSize.height) {
fontSize--;
label.font = [UIFont systemFontOfSize:fontSize];
textSize = [label.text boundingRectWithSize:CGSizeMake(frameSize.width, CGFLOAT_MAX)
options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
attributes:@{NSFontAttributeName : label.font}
context:nil].size;
unrestrictedFrameSize = [label textRectForBounds:CGRectMake(0, 0, label.bounds.size.width, CGFLOAT_MAX) limitedToNumberOfLines:numLines].size;
}
// Set the label frame size back to original
label.frame = originalLabelFrame;
}
Here is the fill code of a UILabel subclass that implements animated font size change:
@interface SNTextLayer : CATextLayer
@end
@implementation SNTextLayer
- (void)drawInContext:(CGContextRef)ctx {
// We override this to make text appear at the same vertical positon as in UILabel
// (otherwise it's shifted tdown)
CGFloat height = self.bounds.size.height;
float fontSize = self.fontSize;
// May need to adjust this somewhat if it's not aligned perfectly in your implementation
float yDiff = (height-fontSize)/2 - fontSize/10;
CGContextSaveGState(ctx);
CGContextTranslateCTM(ctx, 0.0, yDiff);
[super drawInContext:ctx];
CGContextRestoreGState(ctx);
}
@end
@interface SNAnimatableLabel ()
@property CATextLayer* textLayer;
@end
@interface SNAnimatableLabel : UILabel
- (void)animateFontToSize:(CGFloat)fontSize withDuration:(double)duration;
@end
@implementation SNAnimatableLabel
- (void)awakeFromNib {
[super awakeFromNib];
_textLayer = [SNTextLayer new];
_textLayer.backgroundColor = self.backgroundColor.CGColor;
_textLayer.foregroundColor = self.textColor.CGColor;
_textLayer.font = CGFontCreateWithFontName((CFStringRef)self.font.fontName);
_textLayer.frame = self.bounds;
_textLayer.string = self.text;
_textLayer.fontSize = self.font.pointSize;
_textLayer.contentsScale = [UIScreen mainScreen].scale;
[_textLayer setPosition: CGPointMake(CGRectGetMidX(_textLayer.frame), CGRectGetMidY(_textLayer.frame))];
[_textLayer setAnchorPoint: CGPointMake(0.5, 0.5)];
[_textLayer setAlignmentMode: kCAAlignmentCenter];
self.textColor = self.backgroundColor;
// Blend text with background, so that it doens't interfere with textlayer text
[self.layer addSublayer:_textLayer];
self.layer.masksToBounds = NO;
}
- (void)setText:(NSString *)text {
_textLayer.string = text;
super.text = text;
}
- (void)layoutSubviews {
[super layoutSubviews];
// Need to enlarge the frame, otherwise the text may get clipped for bigger font sizes
_textLayer.frame = CGRectInset(self.bounds, -5, -5);
}
- (void)animateFontToSize:(CGFloat)fontSize withDuration:(double)duration {
[CATransaction begin];
[CATransaction setAnimationDuration:duration];
_textLayer.fontSize = fontSize;
[CATransaction commit];
}
精彩评论