我有一些文字,我通过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);
}
答案 0 :(得分:23)
这是一段简单的代码,可以找出最适合框架范围的字体大小:
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];
答案 1 :(得分:9)
我能看到这种可能性的唯一方法是让系统运行大小计算,然后调整大小并重复,直到找到合适的大小。
即。设置一个在某些大小之间的二等分算法。
即。运行它的大小为10。 太小。 大小20。 太小。 大小30。 太大。 大小25。 太小。 大小27。 恰到好处,使用27号。
你甚至可以从数百人开始。
尺寸100。 太大。 大小50。 等...
答案 2 :(得分:6)
目前接受的答案是谈论算法,但iOS提供了NSString对象的计算。
我会使用sizeWithAttributes:
类的NSString
。
<强> sizeWithAttributes:强>
返回使用给定属性绘制时接收器占用的边界框大小。
- (CGSize)sizeWithAttributes:(NSDictionary *)attributes
来源:Apple Docs - NSString UIKit Additions Reference
编辑误解了这个问题,所以这个答案是不合适的。
答案 3 :(得分:4)
您可以使用sizeWithFont:
[myString sizeWithFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:24]
constrainedToSize:CGSizeMake(293, 10000)] // put the size of your frame
但它在iOS 7中已被弃用,因此我建议在UILabel中使用字符串:
[string sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17.0f]}];
如果您正在使用rect:
CGRect textRect = [text boundingRectWithSize:mySize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:FONT}
context:nil];
CGSize size = textRect.size;
答案 4 :(得分:3)
一个小技巧有助于使用sizeWithAttributes:
而无需迭代以获得正确的结果:
NSSize sampleSize = [wordString sizeWithAttributes:
@{ NSFontAttributeName: [NSFont fontWithName:fontName size:fontSize] }];
CGFloat ratio = rect.size.width / sampleSize.width;
fontSize *= ratio;
确保样本的fontSize
足够大,以获得良好的效果。
答案 5 :(得分:3)
以下是完全相同的代码:在某些范围内计算最佳字体大小。此示例位于UITextView
子类的上下文中,因此它使用其边界作为&#34;给定框架&#34;:
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)
}
}
我希望有所帮助。
答案 6 :(得分:2)
答案 7 :(得分:2)
更简单/更快(但当然是近似的)方式将是:
class func calculateOptimalFontSize(textLength:CGFloat, boundingBox:CGRect) -> CGFloat
{
let area:CGFloat = boundingBox.width * boundingBox.height
return sqrt(area / textLength)
}
我们假设每个char都是N x N像素,所以我们只计算N x N进入边界框的次数。
答案 8 :(得分:1)
这是使用其他答案中的逻辑使动态字体大小按帧宽度更改的代码。 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;
答案 9 :(得分:1)
Apple 没有提供任何方法来找出适合给定矩形中文本的字体大小。想法是基于 BinarySearch 找出完美适合给定大小的最佳字体大小。以下扩展尝试不同的字体大小以收敛到完美的字体大小值。
import UIKit
extension UITextView {
@discardableResult func adjustFontToFit(_ rect: CGSize, minFontSize: CGFloat = 5, maxFontSize: CGFloat = 100, accuracy: CGFloat = 0.1) -> CGFloat {
// To avoid text overflow
let targetSize = CGSize(width: floor(rect.width), height: rect.height)
var minFontSize = minFontSize
var maxFontSize = maxFontSize
var fittingSize = targetSize
while maxFontSize - minFontSize > accuracy {
let midFontSize = (minFontSize + maxFontSize) / 2
font = font?.withSize(midFontSize)
fittingSize = sizeThatFits(targetSize)
if fittingSize.height <= rect.height {
minFontSize = midFontSize
} else {
maxFontSize = midFontSize
}
}
// It might be possible that while loop break with last assignment
// to `maxFontSize`, which can overflow the available height
// Using `minFontSize` will be failsafe
font = font?.withSize(minFontSize)
return minFontSize
}
}
答案 10 :(得分:0)
这是一种使用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;
}
答案 11 :(得分:0)
我喜欢@holtwick给出的方法,但发现它有时会高估适合的方法。我创建了一个调整,似乎在我的测试中运行良好。提示:不要忘记测试像#34; WWW&#34;甚至&#34;௵௵௵&#34;
func idealFontSize(for text: String, font: UIFont, width: CGFloat) -> CGFloat {
let baseFontSize = CGFloat(256)
let textSize = text.size(attributes: [NSFontAttributeName: font.withSize(baseFontSize)])
let ratio = width / textSize.width
let ballparkSize = baseFontSize * ratio
let stoppingSize = ballparkSize / CGFloat(2) // We don't want to loop forever, if we've already come down to 50% of the ballpark size give up
var idealSize = ballparkSize
while (idealSize > stoppingSize && text.size(attributes: [NSFontAttributeName: font.withSize(idealSize)]).width > width) {
// We subtract 0.5 because sometimes ballparkSize is an overestimate of a size that will fit
idealSize -= 0.5
}
return idealSize
}
答案 12 :(得分:0)
这是我在swift 4中的解决方案:
private func adjustedFontSizeOf(label: UILabel) -> CGFloat {
guard let textSize = label.text?.size(withAttributes: [.font: label.font]), textSize.width > label.bounds.width else {
return label.font.pointSize
}
let scale = label.bounds.width / textSize.width
let actualFontSize = scale * label.font.pointSize
return actualFontSize
}
我希望它有所帮助。