计算字体大小以适应框架 – 核心文本 – NSAttributedString – iOS

我有一些文本,我正在通过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); } 

我能看到这种可能性的唯一方法是让系统运行尺寸计算,然后调整尺寸并重复,直到find合适的尺寸。

即build立了一个在一定大小之间的平分algorithm。

即运行它的大小10.太小了。 大小20.太小。 大小30.太大了。 大小25.太小。 27号。正好,使用27号。

你甚至可以从数百个开始。

大小100.太大了。 大小50等…

这是一段简单的代码,可以计算出最大的字体大小,以适应帧的范围:

 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]; 

目前接受的答案是谈论一个algorithm,但是iOS提供了一个NSString对象的计算。 我会使用NSString类的sizeWithAttributes:

sizeWithAttributes:

返回使用给定属性绘制的接收器占用的边界框大小。

  - (CGSize)sizeWithAttributes:(NSDictionary *)attributes 

资料来源: Apple Docs – NSString UIKit附加参考

编辑错误地解释了这个问题,所以这个答案是不合格的。

你可以使用sizeWithFont:

 [myString sizeWithFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:24] constrainedToSize:CGSizeMake(293, 10000)] // put the size of your frame 

但是它在iOS 7中已经被弃用,所以我build议如果在UILabel中使用string:

 [string sizeWithAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17.0f]}]; 

如果你正在使用rect:

 CGRect textRect = [text boundingRectWithSize:mySize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:FONT} context:nil]; CGSize size = textRect.size; 

根据Apple的文档,您可以将UILabel的属性adjustsFontSizeToFitWidthYES

这是代码有dynamic字体大小改变的框架宽度,使用其他答案的逻辑。 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; 

更容易/更快(但当然近似)的方式是这样的:

 class func calculateOptimalFontSize(textLength:CGFloat, boundingBox:CGRect) -> CGFloat { let area:CGFloat = boundingBox.width * boundingBox.height return sqrt(area / textLength) } 

我们假设每个字符都是N×N像素,所以我们只计算N×N在边界框内的行进次数。

一个小技巧有助于使用sizeWithAttributes:不需要迭代正确的结果:

 NSSize sampleSize = [wordString sizeWithAttributes: @{ NSFontAttributeName: [NSFont fontWithName:fontName size:fontSize] }]; CGFloat ratio = rect.size.width / sampleSize.width; fontSize *= ratio; 

确保样本的fontSize足够大,以获得良好的结果。

这里有一个方法,似乎适用于使用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; } 

这里是代码,将做到这一点:计算一些范围内的最佳字体大小。 此示例是在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) } } 

我希望有帮助。

我喜欢@holtwick提供的方法,但发现它有时会高估什么会适合。 我创build了一个调整,似乎在我的testing中运行良好。 提示:不要忘记用“WWW”甚至“௵௵௵”

 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 }