计算字体大小以适应框架 - 核心文本 - NSAttributedString - iOS
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20468641/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Calculate Font Size to Fit Frame - Core Text - NSAttributedString - iOS
提问by GuybrushThreepwood
I have some text which I am drawing into a fixed frame via an NSAttributedString (code below). At the moment I am hard coding the text size to 16. My question is, is there a way to calculate the best fit size for the text for the given frame ?
我有一些文本通过 NSAttributedString(下面的代码)绘制到固定框架中。目前我正在将文本大小硬编码为 16。我的问题是,有没有办法计算给定框架文本的最佳适合大小?
- (void)drawText:(CGContextRef)contextP startX:(float)x startY:(float)
y withText:(NSString *)standString
{
CGContextTranslateCTM(contextP, 0, (bottom-top)*2);
CGContextScaleCTM(contextP, 1.0, -1.0);
CGRect frameText = CGRectMake(1, 0, (right-left)*2, (bottom-top)*2);
NSMutableAttributedString * attrString = [[NSMutableAttributedString alloc] initWithString:standString];
[attrString addAttribute:NSFontAttributeName
value:[UIFont fontWithName:@"Helvetica-Bold" size:16.0]
range:NSMakeRange(0, attrString.length)];
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attrString));
struct CGPath * p = CGPathCreateMutable();
CGPathAddRect(p, NULL, frameText);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0,0), p, NULL);
CTFrameDraw(frame, contextP);
}
采纳答案by Fogmeister
The only way I can see this being possible is to have a system that runs the size calculation then adjusts the size and repeats until it finds the right size.
我认为这是可能的唯一方法是让系统运行尺寸计算然后调整尺寸并重复直到找到合适的尺寸。
I.e. set up a bisecting algorithm that goes between certain sizes.
即建立一个在某些大小之间进行的二等分算法。
i.e. run it for size 10. Too small. Size 20. Too small. Size 30. Too big. Size 25. Too small. Size 27. Just right, use size 27.
即运行它的大小 10。太小了。20 码。太小了。30 码。太大了。尺寸 25。太小了。27码。正好,用27码。
You could even start in hundreds.
你甚至可以从数百开始。
Size 100. Too big. Size 50. etc...
100 码。太大了。尺寸 50。等...
回答by user3072402
Here is a simple piece of code that will figure out the maximum font size to fit within the bounds of a frame:
这是一段简单的代码,它将计算出适合框架边界的最大字体大小:
UILabel *label = [[UILabel alloc] initWithFrame:frame];
label.text = @"Some text";
float largestFontSize = 12;
while ([label.text sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:largestFontSize]}].width > modifierFrame.size.width)
{
largestFontSize--;
}
label.font = [UIFont systemFontOfSize:largestFontSize];
回答by CloakedEddy
The currently accepted answer talks of an algorithm, but iOS provides calculations for an NSString object.
I would use sizeWithAttributes:
of the NSString
class.
当前接受的答案讨论了一种算法,但 iOS 提供了对 NSString 对象的计算。我会使用sizeWithAttributes:
这个NSString
类。
sizeWithAttributes:
Returns the bounding box size the receiver occupies when drawn with the given attributes.
- (CGSize)sizeWithAttributes:(NSDictionary *)attributes
大小与属性:
返回使用给定属性绘制时接收器占用的边界框大小。
- (CGSize)sizeWithAttributes:(NSDictionary *)attributes
Source: Apple Docs - NSString UIKit Additions Reference
来源:Apple Docs - NSString UIKit Additions Reference
EDITMisinterpreted the question, so this answer is off the mark.
编辑误解了这个问题,所以这个答案不合时宜。
回答by sabiland
Even more easy/faster (but of course approximate) way would be this:
更简单/更快(但当然是近似)的方法是:
class func calculateOptimalFontSize(textLength:CGFloat, boundingBox:CGRect) -> CGFloat
{
let area:CGFloat = boundingBox.width * boundingBox.height
return sqrt(area / textLength)
}
We are assuming each char is N x N pixels, so we just calculate how many times N x N goes inside bounding box.
我们假设每个字符是 N x N 像素,所以我们只计算 N x N 进入边界框的次数。
回答by Pull
You could use sizeWithFont :
您可以使用 sizeWithFont :
[myString sizeWithFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:24]
constrainedToSize:CGSizeMake(293, 10000)] // put the size of your frame
But it is deprecated in iOS 7, so I recommend if working with string in UILabel :
但它在 iOS 7 中已被弃用,因此我建议在 UILabel 中使用 string :
[string sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17.0f]}];
If you are working with a rect :
如果您正在使用 rect :
CGRect textRect = [text boundingRectWithSize:mySize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:FONT}
context:nil];
CGSize size = textRect.size;
回答by Holtwick
A little trick helps to make use of sizeWithAttributes:
without the need of iterating for the right result:
一个小技巧有助于sizeWithAttributes:
在不需要迭代正确结果的情况下使用:
NSSize sampleSize = [wordString sizeWithAttributes:
@{ NSFontAttributeName: [NSFont fontWithName:fontName size:fontSize] }];
CGFloat ratio = rect.size.width / sampleSize.width;
fontSize *= ratio;
Make sure the fontSize
for the sample is big enough to get good results.
确保fontSize
样本足够大以获得良好的结果。
回答by tadija
Here is code which will do exactly that: calculate optimal font size within some bounds. This sample is in context of UITextView
subclass, so it's using its bounds as a "given frame":
这是可以做到这一点的代码:在某些范围内计算最佳字体大小。此示例在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)
}
}
I hope that helps.
我希望这有帮助。
回答by MuhammadBassio
You can set the UILabel
's property adjustsFontSizeToFitWidth
to YES
as per Apple's documentation
您可以根据Apple 的文档将UILabel
's 属性adjustsFontSizeToFitWidth
设置YES
为
回答by vatti
This is the code to have dynamic font size changing by the frame width, using the logic from the other answers. The while loop might be dangerous, so please donot hesitate to submit improvements.
这是使用其他答案中的逻辑使动态字体大小随框架宽度变化的代码。while 循环可能很危险,所以请不要犹豫提交改进。
float fontSize = 17.0f; //initial font size
CGSize rect;
while (1) {
fontSize = fontSize+0.1;
rect = [watermarkText sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}];
if ((int)rect.width == (int)subtitle1Text.frame.size.width) {
break;
}
}
subtitle1Text.fontSize = fontSize;
回答by Mike
Here's a method that seems to work well for iOS 9 using UITextView
objects. You might have to tweet it a bit for other applications.
这是一种使用UITextView
对象似乎适用于 iOS 9 的方法。您可能需要为其他应用程序发布一些推文。
/*!
* Find the height of the smallest rectangle that will enclose a string using the given font.
*
* @param string The string to check.
* @param font The drawing font.
* @param width The width of the drawing area.
*
* @return The height of the rectngle enclosing the text.
*/
- (float) heightForText: (NSString *) string font: (UIFont *) font width: (float) width {
NSDictionary *fontAttributes = [NSDictionary dictionaryWithObject: font
forKey: NSFontAttributeName];
CGRect rect = [string boundingRectWithSize: CGSizeMake(width, INT_MAX)
options: NSStringDrawingUsesLineFragmentOrigin
attributes: fontAttributes
context: nil];
return rect.size.height;
}
/*!
* Find the largest font size that will allow a block of text to fit in a rectangle of the given size using the system
* font.
*
* The code is tested and optimized for UITextView objects.
*
* The font size is determined to ±0.5. Change delta in the code to get more or less precise results.
*
* @param string The string to check.
* @param size The size of the bounding rectangle.
*
* @return: The font size.
*/
- (float) maximumSystemFontSize: (NSString *) string size: (CGSize) size {
// Hack: For UITextView, the last line is clipped. Make sure it's not one we care about.
if ([string characterAtIndex: string.length - 1] != '\n') {
string = [string stringByAppendingString: @"\n"];
}
string = [string stringByAppendingString: @"M\n"];
float maxFontSize = 16.0;
float maxHeight = [self heightForText: string font: [UIFont systemFontOfSize: maxFontSize] width: size.width];
while (maxHeight < size.height) {
maxFontSize *= 2.0;
maxHeight = [self heightForText: string font: [UIFont systemFontOfSize: maxFontSize] width: size.width];
}
float minFontSize = maxFontSize/2.0;
float minHeight = [self heightForText: string font: [UIFont systemFontOfSize: minFontSize] width: size.width];
while (minHeight > size.height) {
maxFontSize = minFontSize;
minFontSize /= 2.0;
maxHeight = minHeight;
minHeight = [self heightForText: string font: [UIFont systemFontOfSize: minFontSize] width: size.width];
}
const float delta = 0.5;
while (maxFontSize - minFontSize > delta) {
float middleFontSize = (minFontSize + maxFontSize)/2.0;
float middleHeight = [self heightForText: string font: [UIFont systemFontOfSize: middleFontSize] width: size.width];
if (middleHeight < size.height) {
minFontSize = middleFontSize;
minHeight = middleHeight;
} else {
maxFontSize = middleFontSize;
maxHeight = middleHeight;
}
}
return minFontSize;
}