如何调整标签的字体大小以适应矩形?
是的,有这个很酷的myLabel.adjustsFontSizeToFitWidth = YES;
属性。 但只要标签有两行或更多行,就不会将文本调整为任何东西。 所以它只是被截断…如果它不适合rect。
还有另一种方法吗?
如果你想确保标签符合矩形的宽度和高度明智你可以尝试不同的字体大小的标签,看看是否适合。
这段代码从300磅开始,尝试通过减小字体大小来使标签适合目标矩形。
- (void) sizeLabel: (UILabel *) label toRect: (CGRect) labelRect { // Set the frame of the label to the targeted rectangle label.frame = labelRect; // Try all font sizes from largest to smallest font size int fontSize = 300; int minFontSize = 5; // Fit label width wize CGSize constraintSize = CGSizeMake(label.frame.size.width, MAXFLOAT); do { // Set current font size label.font = [UIFont fontWithName:label.font.fontName size:fontSize]; // Find label size for current font size CGRect textRect = [[label text] boundingRectWithSize:constraintSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: label.font} context:nil]; CGSize labelSize = textRect.size; // Done, if created label is within target size if( labelSize.height <= label.frame.size.height ) break; // Decrease the font size and try again fontSize -= 2; } while (fontSize > minFontSize); }
我发现尼尔斯的答案是这个问题的最佳答案。 不过,我有一个UIView,可以有100个标签,我需要适应文本,所以这个过程是非常低效的,我可以感受到性能的打击。
这里是他的代码修改为使用二进制search,而不是一个线性search。 现在它非常有效地工作。
- (NSInteger)binarySearchForFontSizeForLabel:(UILabel *)label withMinFontSize:(NSInteger)minFontSize withMaxFontSize:(NSInteger)maxFontSize withSize:(CGSize)size { // If the sizes are incorrect, return 0, or error, or an assertion. if (maxFontSize < minFontSize) { return 0; } // Find the middle NSInteger fontSize = (minFontSize + maxFontSize) / 2; // Create the font UIFont *font = [UIFont fontWithName:label.font.fontName size:fontSize]; // Create a constraint size with max height CGSize constraintSize = CGSizeMake(size.width, MAXFLOAT); // Find label size for current font size CGRect rect = [label.text boundingRectWithSize:constraintSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : font} context:nil]; CGSize labelSize = rect.size; // EDIT: The next block is modified from the original answer posted in SO to consider the width in the decision. This works much better for certain labels that are too thin and were giving bad results. if (labelSize.height >= (size.height + 10) && labelSize.width >= (size.width + 10) && labelSize.height <= (size.height) && labelSize.width <= (size.width)) { return fontSize; } else if (labelSize.height > size.height || labelSize.width > size.width) { return [self binarySearchForFontSizeForLabel:label withMinFontSize:minFontSize withMaxFontSize:fontSize - 1 withSize:size]; } else { return [self binarySearchForFontSizeForLabel:label withMinFontSize:fontSize + 1 withMaxFontSize:maxFontSize withSize:size]; } } - (void)sizeBinaryLabel:(UILabel *)label toRect:(CGRect)labelRect { // Set the frame of the label to the targeted rectangle label.frame = labelRect; // Try all font sizes from largest to smallest font int maxFontSize = 300; int minFontSize = 5; NSInteger size = [self binarySearchForFontSizeForLabel:label withMinFontSize:minFontSize withMaxFontSize:maxFontSize withSize:label.frame.size]; label.font = [UIFont fontWithName:label.font.fontName size:size]; }
这里的Swift版根据@NielsCastle的答案,使用二进制search
extension UILabel{ func adjustFontSizeToFitRect(rect : CGRect){ if text == nil{ return } frame = rect let maxFontSize: CGFloat = 100.0 let minFontSize: CGFloat = 5.0 var q = Int(maxFontSize) var p = Int(minFontSize) let constraintSize = CGSize(width: rect.width, height: CGFloat.max) while(p <= q){ let currentSize = (p + q) / 2 font = font.fontWithSize( CGFloat(currentSize) ) let text = NSAttributedString(string: self.text!, attributes: [NSFontAttributeName:font]) let textRect = text.boundingRectWithSize(constraintSize, options: .UsesLineFragmentOrigin, context: nil) let labelSize = textRect.size if labelSize.height < frame.height && labelSize.height >= frame.height-10 && labelSize.width < frame.width && labelSize.width >= frame.width-10 { break }else if labelSize.height > frame.height || labelSize.width > frame.width{ q = currentSize - 1 }else{ p = currentSize + 1 } } } }
用法
label.adjustFontSizeToFitRect(rect)
往往只是
label.adjustFontSizeToFitRect(rect.frame)
此解决scheme(基于此答案 )与自动布局一起工作,并执行二进制search以查找最佳字体大小。
我发现唯一的警告是,你不能指定行数(因为AFAIK你不能指定boundingRectWithSize
你想要多less行)。
AdjustableLabel.h
#import <UIKit/UIKit.h> @interface AdjustableLabel : UILabel /** If set to YES, font size will be automatically adjusted to frame. Note: numberOfLines can't be specified so it will be set to 0. */ @property(nonatomic) BOOL adjustsFontSizeToFitFrame; @end
AdjustableLabel.m
#import "AdjustableLabel.h" @interface AdjustableLabel () @property(nonatomic) BOOL fontSizeAdjusted; @end // The size found S satisfies: S fits in the frame and and S+DELTA doesn't. #define DELTA 0.5 @implementation AdjustableLabel - (void)setAdjustsFontSizeToFitFrame:(BOOL)adjustsFontSizeToFitFrame { _adjustsFontSizeToFitFrame = adjustsFontSizeToFitFrame; if (adjustsFontSizeToFitFrame) { self.numberOfLines = 0; // because boundingRectWithSize works like this was 0 anyway } } - (void)layoutSubviews { [super layoutSubviews]; if (self.adjustsFontSizeToFitFrame && !self.fontSizeAdjusted) { self.fontSizeAdjusted = YES; // to avoid recursion, because adjustFontSizeToFrame will trigger this method again [self adjustFontSizeToFrame]; } } - (void) adjustFontSizeToFrame { UILabel* label = self; if (label.text.length == 0) return; // Necessary or single-char texts won't be correctly adjusted BOOL checkWidth = label.text.length == 1; CGSize labelSize = label.frame.size; // Fit label width-wise CGSize constraintSize = CGSizeMake(checkWidth ? MAXFLOAT : labelSize.width, MAXFLOAT); // Try all font sizes from largest to smallest font size CGFloat maxFontSize = 300; CGFloat minFontSize = 5; NSString* text = label.text; UIFont* font = label.font; while (true) { // Binary search between min and max CGFloat fontSize = (maxFontSize + minFontSize) / 2; // Exit if approached minFontSize enough if (fontSize - minFontSize < DELTA/2) { font = [UIFont fontWithName:font.fontName size:minFontSize]; break; // Exit because we reached the biggest font size that fits } else { font = [UIFont fontWithName:font.fontName size:fontSize]; } // Find label size for current font size CGRect rect = [text boundingRectWithSize:constraintSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : font} context:nil]; // Now we discard a half if( rect.size.height <= labelSize.height && (!checkWidth || rect.size.width <= labelSize.width) ) { minFontSize = fontSize; // the best size is in the bigger half } else { maxFontSize = fontSize; // the best size is in the smaller half } } label.font = font; } @end
用法
AdjustableLabel* label = [[AdjustableLabel alloc] init]; label.adjustsFontSizeToFitFrame = YES; // In case you change the font, the size you set doesn't matter label.font = [UIFont fontWithName:@"OpenSans-Light" size:20];
如果有人正在寻找一个MonoTouch / Xamarin.iOS的实现,就像我做的那样…在这里你去:
private int BinarySearchForFontSizeForText(NSString text, int minFontSize, int maxFontSize, SizeF size) { if (maxFontSize < minFontSize) return minFontSize; int fontSize = (minFontSize + maxFontSize) / 2; UIFont font = UIFont.BoldSystemFontOfSize(fontSize); var constraintSize = new SizeF(size.Width, float.MaxValue); SizeF labelSize = text.StringSize(font, constraintSize, UILineBreakMode.WordWrap); if (labelSize.Height >= size.Height + 10 && labelSize.Width >= size.Width + 10 && labelSize.Height <= size.Height && labelSize.Width <= size.Width) return fontSize; else if (labelSize.Height > size.Height || labelSize.Width > size.Width) return BinarySearchForFontSizeForText(text, minFontSize, fontSize - 1, size); else return BinarySearchForFontSizeForText(text, fontSize + 1, maxFontSize, size); } private void SizeLabelToRect(UILabel label, RectangleF labelRect) { label.Frame = labelRect; int maxFontSize = 300; int minFontSize = 5; int size = BinarySearchForFontSizeForText(new NSString(label.Text), minFontSize, maxFontSize, label.Frame.Size); label.Font = UIFont.SystemFontOfSize(size); }
这是一个从Objective-C到C#的agarcian代码的翻译,只做了一些小小的修改:由于返回的结果总是为0( 请参阅borked的注释 ),我将返回计算的minFontSize,这会导致正确的字体大小。
这是一个UILabel的Swift扩展。 它运行二进制searchalgorithm来调整标签的字体和边界的大小,并且testing适用于iOS 9。
使用方法:调整字体大小以适应100×100的大小(1.0字体内准确)。
<label>.fitFontForSize(CGSizeMake(100.0, 100.0))
将以下内容复制/粘贴到您的文件中
extension UILabel { func fitFontForSize(constrainedSize : CGSize, var maxFontSize : CGFloat = 300.0, var minFontSize : CGFloat = 5.0, accuracy : CGFloat = 1.0) { assert(maxFontSize > minFontSize) 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() } }
注意:标签的宽度和高度限制不应该被设置为了这个工作。 一个简单的解决方法是将标签embedded到具有设置大小限制的UIView中,然后调用上面定义的函数(如下所示)。
<label>.fitFontForSize(<superview>.bounds.size)
还设置myLabel.numberOfLines = 10
或任何你想要的最大数量的行。
所有这些都是对原始问题的有趣解决scheme,但是它们都缺less一个重要的东西:如果你完全依靠familyName来获得下一个字体来testing,那么你就失去了重量信息和可能更高级的属性,比如小帽子,身材风格等
一种更好的方法是不用传递字体名称,而是在[UIFont fontWithName:someFontName size:someFontSize]
中传递UIFontDescriptor
对象,然后执行[UIFont fontWithDescriptor:someFontDescriptor size:someFontSize]
。
尼尔斯城堡代码工作发现。
这是一个不同的实现相同的想法。
我的解决scheme更精确,但也更多的CPU密集型。
将此函数添加到inheritanceUILabel的类。
-(void)fitCurrentFrame{ CGSize iHave = self.frame.size; BOOL isContained = NO; do{ CGSize iWant = [self.text sizeWithFont:self.font]; if(iWant.width > iHave.width || iWant.height > iHave.height){ self.font = [UIFont fontWithName:self.font.fontName size:self.font.pointSize - 0.1]; isContained = NO; }else{ isContained = YES; } }while (isContained == NO); }
我已经根据@ agarcian的答案创build了UILabel的类别。 但我计算fontSize取决于屏幕上绘制文本所需的平方。 这种方法不需要循环,计算是通过一次迭代完成的。
这里的.h文件:
// UILabel+Extended.h // Created by Firuz on 16/08/14. // Copyright (c) 2014. All rights reserved. #import <UIKit/UIKit.h> @interface UILabel (Extended) /** This method calculate the optimal font size for current number of lines in UILable. Mus be called after drawing UILabel view */ - (NSInteger)fontSizeWithMinFontSize:(NSInteger)minFontSize withMaxFontSize:(NSInteger)maxFontSize; @end
这里的.m文件:
// UILabel+Extended.m // Created by Firuz on 16/08/14. // Copyright (c) 2014. All rights reserved. #import "UILabel+Extended.h" @implementation UILabel (Extended) - (NSInteger)fontSizeWithMinFontSize:(NSInteger)minFontSize withMaxFontSize:(NSInteger)maxFontSize { if (maxFontSize < minFontSize) { return 0; } UIFont *font = [UIFont fontWithName:self.font.fontName size:maxFontSize]; CGFloat lineHeight = [font lineHeight]; CGSize constraintSize = CGSizeMake(MAXFLOAT, lineHeight); CGRect rect = [self.text boundingRectWithSize:constraintSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : font} context:nil]; CGFloat labelSqr = self.frame.size.width * self.frame.size.height; CGFloat stringSqr = rect.size.width/self.frame.size.width * (lineHeight + font.pointSize) * self.frame.size.width; CGFloat multiplyer = labelSqr/stringSqr; if (multiplyer < 1) { if (minFontSize < maxFontSize*multiplyer) { return maxFontSize * multiplyer; } else { return minFontSize; } } return maxFontSize; } @end
所有二进制search都是好的,但是使用帧检查来停止recursion不是那么合乎逻辑。 更好地检查字体大小,导致UIFont支持浮点大小,这种字体更适合。 另外使用标签段落样式来准确计算大小。
如果有人有兴趣,可以看下面的代码:
static UIFont * ___suitableFontInRangePrivate(const CGSize labelSize, NSParagraphStyle * paragraphStyle, NSString * fontName, NSString * text, const CGFloat minSize, const CGFloat maxSize) { // Font size in range, middle size between max & min. const CGFloat currentSize = minSize + ((maxSize - minSize) / 2); // Font with middle size. UIFont * currentFont = [UIFont fontWithName:fontName size:currentSize]; // Calculate text height. const CGFloat textHeight = [text boundingRectWithSize:CGSizeMake(labelSize.width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{ NSFontAttributeName : currentFont, NSParagraphStyleAttributeName : paragraphStyle } context:nil].size.height; CGFloat min, max; if (textHeight > labelSize.height) { // Take left range part. min = minSize; max = currentSize; } else { // Take right range part. min = currentSize; max = maxSize; } // If font size in int range [0.0; 2.0] - got it, othervice continue search. return ((max - min) <= 2.0) ? currentFont : ___suitableFontInRangePrivate(labelSize, paragraphStyle, fontName, text, min, max); } void UILabelAdjustsFontSizeToFrame(UILabel * label) { if (!label) return; NSString * text = [label text]; __block NSParagraphStyle * style = nil; [[label attributedText] enumerateAttributesInRange:NSMakeRange(0, [text length]) options:(NSAttributedStringEnumerationOptions)0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop){ id paragraphStyle = [attrs objectForKey:@"NSParagraphStyle"]; if (paragraphStyle) style = [paragraphStyle retain]; }]; if (!style) { NSMutableParagraphStyle * paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; if (!paragraphStyle) paragraphStyle = [[NSMutableParagraphStyle alloc] init]; if (paragraphStyle) { [paragraphStyle setLineBreakMode:[label lineBreakMode]]; [paragraphStyle setAlignment:[label textAlignment]]; } style = paragraphStyle; } UIFont * suitableFont = ___suitableFontInRangePrivate([label frame].size, style, [[label font] fontName], text, 0, 500); [label setFont:suitableFont]; [style release]; }
由于我没有find一个工作解决scheme来回答我所有的需求,我已经创build了自己的组件,提供以下function: FittableFontLabel
- 使用多线标签时,调整字体以适合高度和宽度
- 使用单行标签调整字体以适应宽度时,高度标签将自行resize
- 支持
NSAttributedStrings
以及基本string - 更改标签文本/框架时自动resize
- …
如果你们中的任何一个人都很有趣,那么使用CocoaPods就是一个完整的swift库: https : //github.com/tbaranes/FittableFontLabel
Swift 3“二进制search解决scheme”基于这个答案进行了小幅改进。 示例是在UITextView
子类的上下文中:
func binarySearchOptimalFontSize(min: Int, max: Int) -> Int { let middleSize = (min + max) / 2 if min > max { return middleSize } let middleFont = UIFont(name: font!.fontName, size: CGFloat(middleSize))! let attributes = [NSFontAttributeName : middleFont] let attributedString = NSAttributedString(string: text, attributes: attributes) let size = CGSize(width: bounds.width, height: .greatestFiniteMagnitude) let options: NSStringDrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading] let textSize = attributedString.boundingRect(with: size, options: options, context: nil) if textSize.size.equalTo(bounds.size) { return middleSize } else if (textSize.height > bounds.size.height || textSize.width > bounds.size.width) { return binarySearchOptimalFontSize(min: min, max: middleSize - 1) } else { return binarySearchOptimalFontSize(min: middleSize + 1, max: max) } }
我希望能帮助别人。
@ agarcian的答案很接近,但对我来说并不是那么有用,就像其他人在评论中提到的那样,它总是返回0。
这是我的尝试。
干杯!
/** * Returns the font size required in order to fit the specified text in the specified area. * NB! When drawing, be sure to pass in the same options that we pass to boundingRectWithSize:options:attributes:context: * Heavily modified form of: http://stackoverflow.com/a/14662750/1027452 */ +(NSInteger)fontSizeForText:(NSString *)text withFont:(UIFont *)font inArea:(CGSize)areaSize minFontSize:(NSInteger)minFontSize maxFontSize:(NSInteger)maxFontSize { // If the sizes are incorrect, return 0, or error, or an assertion. if (maxFontSize < minFontSize) { return 0; } // Find the middle NSInteger fontSize = (minFontSize + maxFontSize) / 2; // Create the font UIFont *f = [UIFont fontWithName:font.fontName size:fontSize]; // Create a constraint size with max height CGSize constraintSize = CGSizeMake(areaSize.width, MAXFLOAT); // Find label size for current font size CGRect rect = [text boundingRectWithSize:constraintSize options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) attributes:@{NSFontAttributeName : f} context:nil]; CGSize labelSize = rect.size; if (labelSize.height <= areaSize.height && labelSize.width <= areaSize.width ) { return fontSize; } else if (labelSize.height > areaSize.height || labelSize.width > areaSize.width) { return [self fontSizeForText:text withFont:f inArea:areaSize minFontSize:minFontSize maxFontSize:maxFontSize -1];; } else { return [self fontSizeForText:text withFont:f inArea:areaSize minFontSize:minFontSize+1 maxFontSize:maxFontSize];; } }