Расчет размера текста UILabel
Я рисую UILabels
программно. Они получают свои размеры из базы данных. Так что я не могу просто использовать sizeToFit
, Я уже реализовал функцию, которая перерисовывает UILabels
с пройденным соотношением. Так что все, что мне нужно найти, это текст на UILabel
с моей точки зрения, что потребуется максимальное отношение к перерисовать UILabels
, Итак, наконец, мне нужно сделать что-то вроде этого:
double ratio = 1.00;
for (UILabel* labels in sec.subviews) {
float widthLabel = labels.frame.size.width;
float heightLabel = labels.frame.size.height;
float heightText = //get the text height here
float widthText = //get the text width here
if (widthLabel < widthText) {
ratio = MAX(widthText/widthLabel,ratio);
}
if (heightLabel < heightText) {
ratio = MAX(heightText/heightLabel, ratio);
}
}
//redraw UILabels with the given ratio here
Итак, как я могу получить размер текста по высоте и ширине, так как часть моего текста не помещается в метку, я не могу просто использовать границы метки? Я использую Xcode 5 и iOS 7.
12 ответов
Проблема с
CGRect r = [text boundingRectWithSize:CGSizeMake(200, 0)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}
context:nil];
является boundingRectWithSize
который определяет максимальное значение, которое может иметь CGRect.
Мое решение этой проблемы состоит в том, чтобы проверить, превышает ли оно, если нет, то текст может вписаться в метку. Я сделал это с помощью петель.
NSString *text = @"This is a long sentence. Wonder how much space is needed?";
CGFloat width = 100;
CGFloat height = 100;
bool sizeFound = false;
while (!sizeFound) {
NSLog(@"Begin loop");
CGFloat fontSize = 14;
CGFloat previousSize = 0.0;
CGFloat currSize = 0.0;
for (float fSize = fontSize; fSize < fontSize+6; fSize++) {
CGRect r = [text boundingRectWithSize:CGSizeMake(width, height)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fSize]}
context:nil];
currSize =r.size.width*r.size.height;
if (previousSize >= currSize) {
width = width*11/10;
height = height*11/10;
fSize = fontSize+10;
}
else {
previousSize = currSize;
}
NSLog(@"fontSize = %f\tbounds = (%f x %f) = %f",
fSize,
r.size.width,
r.size.height,r.size.width*r.size.height);
}
if (previousSize == currSize) {
sizeFound = true;
}
}
NSLog(@"Size found with width %f and height %f", width, height);
После каждой итерации размер высоты и ширины увеличивается на 10% от ее значения.
Причина, по которой я выбрал 6, заключается в том, что я не хотел, чтобы лейбл был слишком мягким.
Для решения, которое не использует циклы:
NSString *text = @"This is a long sentence. Wonder how much space is needed?";
CGFloat width = 100;
CGFloat height = 100;
CGFloat currentFontSize = 12;
CGRect r1 = [text boundingRectWithSize:CGSizeMake(width, height)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:currentFontSize+6]}
context:nil];
CGRect r2 = [text boundingRectWithSize:CGSizeMake(width, height)
options:NSStringDrawingUsesFontLeading
attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:currentFontSize+6]}
context:nil];
CGFloat firstVal =r1.size.width*r1.size.height;
CGFloat secondVal =r2.size.width*r2.size.height;
NSLog(@"First val %f and second val is %f", firstVal, secondVal);
if (secondVal > firstVal) {
float initRat = secondVal/firstVal;
float ratioToBeMult = sqrtf(initRat);
width *= ratioToBeMult;
height *= ratioToBeMult;
}
NSLog(@"Final width %f and height %f", width, height);
//for verifying
for (NSNumber *n in @[@(12.0f), @(14.0f), @(17.0f)]) {
CGFloat fontSize = [n floatValue];
CGRect r = [text boundingRectWithSize:CGSizeMake(width, height)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}
context:nil];
NSLog(@"fontSize = %f\tbounds = (%f x %f) = %f",
fontSize,
r.size.width,
r.size.height,r.size.width*r.size.height);
firstVal =r.size.width*r.size.height;
}
Где последний цикл является доказательством того, что более крупный шрифт может дать более высокий размер результата.
Все [NSString sizeWithFont...]
методы устарели в iOS 7. Используйте это вместо.
CGRect labelRect = [text
boundingRectWithSize:labelSize
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{
NSFontAttributeName : [UIFont systemFontOfSize:14]
}
context:nil];
Также см. https://developer.apple.com/documentation/foundation/nsstring/1619914-sizewithfont.
UPDATE - пример вывода boundingRectWithSize
На ваш комментарий я сделал простой тест. Код и вывод ниже.
// code to generate a bounding rect for text at various font sizes
NSString *text = @"This is a long sentence. Wonder how much space is needed?";
for (NSNumber *n in @[@(12.0f), @(14.0f), @(18.0f)]) {
CGFloat fontSize = [n floatValue];
CGRect r = [text boundingRectWithSize:CGSizeMake(200, 0)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]}
context:nil];
NSLog(@"fontSize = %f\tbounds = (%f x %f)",
fontSize,
r.size.width,
r.size.height);
}
это приводит к следующему выводу (обратите внимание, что границы меняются, как и ожидалось, с увеличением размера шрифта):
fontSize = 12.000000 bounds = (181.152008 x 28.632000)
fontSize = 14.000000 bounds = (182.251999 x 50.105999)
fontSize = 18.000000 bounds = (194.039993 x 64.421997)
Длина получает количество символов. Если вы хотите получить ширину текста:
Objective-C
CGSize textSize = [label.text sizeWithAttributes:@{NSFontAttributeName:[label font]}];
Swift 4
let size = label.text?.size(withAttributes: [.font: label.font]) ?? .zero
Это дает вам размер. И вы можете сравнить textSize.width
каждого ярлыка.
Еще один простой способ сделать это, о котором я еще не говорил:
CGSize textSize = [label intrinsicContentSize];
(Конечно, это работает правильно только после того, как вы установили текст и шрифт метки.)
Вот быстрый вариант.
let font = UIFont(name: "HelveticaNeue", size: 25)!
let text = "This is some really long text just to test how it works for calculating heights in swift of string sizes. What if I add a couple lines of text?"
let textString = text as NSString
let textAttributes = [NSFontAttributeName: font]
textString.boundingRectWithSize(CGSizeMake(320, 2000), options: .UsesLineFragmentOrigin, attributes: textAttributes, context: nil)
Маленькие советы, ребята, если, как я, вы используете, boundingRectWithSize
с [UIFont systemFontOFSize:14]
Если ваша строка длиной в две строки, возвращаемая высота прямоугольника будет примерно 33,4 точки.
Не делайте ошибку, как я, чтобы превратить его в int
, потому что 33,4 становится 33, а отметка высоты 33 балла проходит от двух до одной строки!
Решение, которое работает с многострочными метками (Swift 4), чтобы вычислить высоту из фиксированной ширины:
let label = UILabel(frame: .zero)
label.numberOfLines = 0 // multiline
label.font = UIFont.systemFont(ofSize: UIFont.labelFontSize) // your font
label.preferredMaxLayoutWidth = width // max width
label.text = "This is a sample text.\nWith a second line!" // the text to display in the label
let height = label.intrinsicContentSize.height
Используя эту строку кода, мы можем получить размер текста на этикетке.
let str = "Sample text"
let size = str.sizeWithAttributes([NSFontAttributeName:UIFont.systemFontOfSize(17.0)])
Таким образом, мы можем использовать как ширину, так и высоту.
Это действительно уродливый беспорядок, учитывая, что если вы устанавливаете шрифт UILabel после того, как вы установили его с помощью attributeString, он стирает информацию о шрифте в атрибутированном тексте, и вам приходится вычислять на основе атрибутов text + font.
Что-то на мелодию
CGFloat promptLabelMaxWidth = self.promptLabel.frame.size.width;
NSAttributedString *attributedText = self.promptLabel.attributedText;
assert(attributedText);
CGRect rect = [attributedText boundingRectWithSize:(CGSize){promptLabelMaxWidth, CGFLOAT_MAX} options: NSStringDrawingUsesLineFragmentOrigin context:nil];
NSString *text = self.promptLabel.text;
UIFont *font = self.promptLabel.font;
if (font) {
CGRect r = [text boundingRectWithSize: CGSizeMake(promptLabelMaxWidth, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName: font}
context:nil];
if (r.size.height > rect.size.height) {
rect = r;
}
}
Swift 3.0
func getLabelHeight() -> CGFloat {
let font = UIFont(name: "OpenSans", size: 15)!
let textString = "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." as NSString
let textAttributes = [NSFontAttributeName: font]
let rect = textString.boundingRect(with: CGSize(width: 320, height: 2000), options: .usesLineFragmentOrigin, attributes: textAttributes, context: nil)
return rect.size.height
}
Свифт 5:
func getTextBounds(_ label : UILabel) -> CGRect {
if label.text != nil && label.font != nil {
return label.text!.boundingRect(
with: CGSize(width: 450, height: 44),
options: [],
attributes: [NSAttributedString.Key.font : label.font!],
context: nil)
}
return CGRect.null
}
func getTextBounds(_ textField : UITextField) -> CGRect {
if textField.text != nil && textField.font != nil {
return textField.text!.boundingRect(
with: CGSize(width: 450, height: 44),
options: [],
attributes: [NSAttributedString.Key.font : textField.font!],
context: nil)
}
return CGRect.null
}
Или, как расширения:
extension UILabel {
func textBounds() -> CGRect {
if self.text != nil && self.font != nil {
return self.text!.boundingRect(
with: CGSize(width: 450, height: 44),
options: [],
attributes: [NSAttributedString.Key.font : self.font!],
context: nil)
}
return CGRect.null
}
}
extension UITextField {
func textBounds() -> CGRect {
if self.text != nil && self.font != nil {
return self.text!.boundingRect(
with: CGSize(width: 450, height: 44),
options: [],
attributes: [NSAttributedString.Key.font : self.font!],
context: nil)
}
return CGRect.null
}
}
msgStr строка получить размер:
let msgStr:NSString = Data["msg"]! as NSString
let messageSize = msgStr.boundingRect(with: CGSize(width: ChatTable.frame.width-116, height: CGFloat.infinity), options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName:UIFont(name: "Montserrat-Light", size: 14)!], context: nil).size