Вычислить размер шрифта для рамки рамки - основной текст - 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);
}
Ответы
Ответ 1
Единственный способ, которым я могу видеть, что это возможно, - это иметь систему, которая выполняет вычисление размера, а затем корректирует размер и повторяет до тех пор, пока не найдет нужный размер.
т.е. установите алгоритм деления пополам, который идет между определенными размерами.
то есть. запустите его для размера 10.
Слишком маленький.
Размер 20.
Слишком маленький.
Размер 30.
Слишком большой.
Размер 25.
Слишком маленький.
Размер 27.
Правильно, используйте размер 27.
Вы даже можете начать сотнями.
Размер 100.
Слишком большой.
Размер 50.
и т.д...
Ответ 2
Вот простой фрагмент кода, который будет определять максимальный размер шрифта в пределах рамки:
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];
Ответ 3
В настоящее время ответы на переговоры по алгоритму, но iOS предоставляет вычисления для объекта NSString.
Я бы использовал sizeWithAttributes:
класса NSString
.
sizeWithAttributes:
Возвращает размер ограничивающей рамки, который занимает приемник при рисовании с заданными атрибутами.
- (CGSize)sizeWithAttributes:(NSDictionary *)attributes
Источник: Apple Docs - ссылка NCCtring UIKit дополнения
РЕДАКТИРОВАТЬ Непоследовательность вопроса, поэтому этот ответ не соответствует значению.
Ответ 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;
Ответ 5
Вы можете установить свойство UILabel
adjustsFontSizeToFitWidth
на YES
согласно документация Apple
Ответ 6
Это код для изменения размера динамического шрифта по ширине кадра, используя логику из других ответов. Цикл 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;
Ответ 7
Небольшой трюк помогает использовать sizeWithAttributes:
без необходимости повторения для правильного результата:
NSSize sampleSize = [wordString sizeWithAttributes:
@{ NSFontAttributeName: [NSFont fontWithName:fontName size:fontSize] }];
CGFloat ratio = rect.size.width / sampleSize.width;
fontSize *= ratio;
Убедитесь, что образец fontSize
для образца достаточно большой, чтобы получить хорошие результаты.
Ответ 8
Еще более простой/быстрый (но, конечно, приблизительный) способ:
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 входит в ограничительную рамку.
Ответ 9
Здесь метод, который, кажется, хорошо работает для iOS 9 с использованием объектов UITextView
. Возможно, вам придется немного почитать его для других приложений.
/*!
* 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 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;
}
Ответ 10
Вот код, который будет делать именно это: рассчитать оптимальный размер шрифта в определенных пределах. Этот пример находится в контексте подкласса 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)
}
}
Я надеюсь, что это поможет.
Ответ 11
Мне нравится подход, данный @holtwick, но обнаружил, что он иногда переоценивает то, что подходит. Я создал настройку, которая, кажется, хорошо работает в моих тестах. Совет. Не забудьте проверить действительно широкие буквы типа "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
}