Визначте розмір UILabel на основі String in Swift


182

Я намагаюся обчислити висоту UILabel на основі різної довжини рядка.

func calculateContentHeight() -> CGFloat{
    var maxLabelSize: CGSize = CGSizeMake(frame.size.width - 48, CGFloat(9999))
    var contentNSString = contentText as NSString
    var expectedLabelSize = contentNSString.boundingRectWithSize(maxLabelSize, options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: UIFont.systemFontOfSize(16.0)], context: nil)
    print("\(expectedLabelSize)")
    return expectedLabelSize.size.height

}

Вище є поточна функція, яку я використовую для визначення висоти, але вона не працює. Я дуже вдячний за будь-яку допомогу, яку я можу отримати. Я б відповів на відповідь у Swift, а не в C.


дублюючі спробувати цей stackoverflow.com/a/61887135/6314955
Malith Kuruwita

Відповіді:


517

Використовуйте розширення на String

Швидкий 3

extension String {
    func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)

        return ceil(boundingBox.height)
    }

    func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)

        return ceil(boundingBox.width)
    }
}

а також на NSAttributedString(що дуже корисно часом)

extension NSAttributedString {
    func height(withConstrainedWidth width: CGFloat) -> CGFloat {
        let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
        let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil)

        return ceil(boundingBox.height)
    }

    func width(withConstrainedHeight height: CGFloat) -> CGFloat {
        let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
        let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil)

        return ceil(boundingBox.width)
    }
}

Швидкий 4

Просто змініть значення attributesв extension Stringметодах

з

[NSFontAttributeName: font]

до

[.font : font]

2
@CodyWeaver перевірте метод редагування для widthWithConstrainedHeight.
Каан Дедеоглу

7
@KaanDedeoglu, як це буде працювати з динамічними рядками висоти, як, наприклад, при використанні "numberOfLines" = 0 (що може бути конкретним UILabel, не впевнений) або lineBreakMode ByWordWrapping. Я здогадувався, щоб додати такі атрибути, [NSFontAttributeName: font, NSLineBreakMode: .ByWordWrapping]але це не спрацювало
Francisc0

1
Думаю, я зрозумів свою відповідь. Мені потрібно використовувати NSParagraphStyleAttributeName : styleтам, де стиль - NSMutableParagraphStyle
Francisc0

4
Мені потрібно написати "self.boundingRect" замість "borderingRect", інакше я отримаю помилку компіляції.
Майк М

1
Одне з цієї відповіді, яку я знайшов, коли я використовував її в, sizeForItemAtIndexPath- UICollectionViewце те, що, здається, перекриє прибутокinsetForSectionAt
Зак Шапіро

15

Для багаторядкового тексту ця відповідь працює неправильно. Ви можете створити інше розширення String за допомогою UILabel

extension String {
func height(constraintedWidth width: CGFloat, font: UIFont) -> CGFloat {
    let label =  UILabel(frame: CGRect(x: 0, y: 0, width: width, height: .greatestFiniteMagnitude))
    label.numberOfLines = 0
    label.text = self
    label.font = font
    label.sizeToFit()

    return label.frame.height
 }
}

UILabel отримує фіксовану ширину, а .numberOfLines встановлюється 0. Додаючи текст і викликаючи .sizeToFit (), він автоматично підлаштовується під потрібну висоту.

Код написано Swift 3 🔶🐦


15
sizeToFit однак вводить мільйон випусків через багато пропусків малюнка. Розрахувати розмір вручну - це значно дешевше в ресурсах
Марко Паппалардо

2
Це рішення повинно встановити UIFont UILabel для забезпечення правильної висоти.
Метью Спенсер

для мене прекрасно працює - навіть рахунки для порожніх рядків (на відміну від прийнятої відповіді). Дуже зручно для обчислення висоти tableView з автоматичними осередками висоти!
Хендіс

Я виявив, що прийнята відповідь працює на фіксовану ширину, але не на фіксовану висоту. Для фіксованої висоти було б просто збільшити ширину, щоб вмістити все на одному рядку, якщо тільки в тексті не було розриву рядка. Ось моя альтернативна відповідь: Мій відповідь
MSimic

2
Я опублікував подібне рішення - без потреби дзвінка доsizeToFit
RyanG

6

Ось просте рішення, яке працює для мене ... схоже на деякі з інших розміщених, але воно не включає потреби в дзвінках sizeToFit

Зверніть увагу, це написано в Swift 5

let lbl = UILabel()
lbl.numberOfLines = 0
lbl.font = UIFont.systemFont(ofSize: 12) // make sure you set this correctly 
lbl.text = "My text that may or may not wrap lines..."

let width = 100.0 // the width of the view you are constraint to, keep in mind any applied margins here

let height = lbl.systemLayoutSizeFitting(CGSize(width: width, height: UIView.layoutFittingCompressedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel).height

Це обробляє обертання ліній і таке інше. Не найвишуканіший код, але він виконує роботу.


2
extension String{

    func widthWithConstrainedHeight(_ height: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: height)

        let boundingBox = self.boundingRect(with: constraintRect, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)

        return ceil(boundingBox.width)
    }

    func heightWithConstrainedWidth(_ width: CGFloat, font: UIFont) -> CGFloat? {
        let constraintRect = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(with: constraintRect, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)

        return ceil(boundingBox.height)
    }

}

1

Це моя відповідь у Swift 4.1 та Xcode 9.4.1

//This is your label
let proNameLbl = UILabel(frame: CGRect(x: 0, y: 20, width: 300, height: height))
proNameLbl.text = "This is your text"
proNameLbl.font = UIFont.systemFont(ofSize: 17)
proNameLbl.numberOfLines = 0
proNameLbl.lineBreakMode = .byWordWrapping
infoView.addSubview(proNameLbl)

//Function to calculate height for label based on text
func heightForView(text:String, font:UIFont, width:CGFloat) -> CGFloat {
    let label:UILabel = UILabel(frame: CGRect(x: 0, y: 0, width: width, height: CGFloat.greatestFiniteMagnitude))
    label.numberOfLines = 0
    label.lineBreakMode = NSLineBreakMode.byWordWrapping
    label.font = font
    label.text = text

    label.sizeToFit()
    return label.frame.height
}

Тепер ви викликаєте цю функцію

//Call this function
let height = heightForView(text: "This is your text", font: UIFont.systemFont(ofSize: 17), width: 300)
print(height)//Output : 41.0

1

Я виявив, що прийнята відповідь працює на фіксовану ширину, але не на фіксовану висоту. Для фіксованої висоти було б просто збільшити ширину, щоб вмістити все на одному рядку, якщо тільки в тексті не було розриву рядка.

Функція ширини викликає функцію висоти кілька разів, але це швидкий розрахунок, і я не помітив проблем з використанням функції в рядках UITable.

extension String {

    public func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font : font], context: nil)

        return ceil(boundingBox.height)
    }

    public func width(withConstrainedHeight height: CGFloat, font: UIFont, minimumTextWrapWidth:CGFloat) -> CGFloat {

        var textWidth:CGFloat = minimumTextWrapWidth
        let incrementWidth:CGFloat = minimumTextWrapWidth * 0.1
        var textHeight:CGFloat = self.height(withConstrainedWidth: textWidth, font: font)

        //Increase width by 10% of minimumTextWrapWidth until minimum width found that makes the text fit within the specified height
        while textHeight > height {
            textWidth += incrementWidth
            textHeight = self.height(withConstrainedWidth: textWidth, font: font)
        }
        return ceil(textWidth)
    }
}

1
що таке minimumTextWrapWidth:CGFloat?
В’ячаслав Герчичов

Це просто значення насіння для обчислень у функції. Якщо ви очікуєте, що ширина буде великою, вибір невеликого мінімальногоTextWrapWidth призведе до того, що цикл час пройде через додаткові ітерації. Отже, чим більша мінімальна ширина, тим краще, але якщо вона більша за потрібну фактичну ширину, то вона завжди буде повертаною шириною.
MSimic

0

Перевірте висоту тексту етикетки і вона над цим працює

let labelTextSize = ((labelDescription.text)! as NSString).boundingRect(
                with: CGSize(width: labelDescription.frame.width, height: .greatestFiniteMagnitude),
                options: .usesLineFragmentOrigin,
                attributes: [.font: labelDescription.font],
                context: nil).size
            if labelTextSize.height > labelDescription.bounds.height {
                viewMoreOrLess.hide(byHeight: false)
                viewLess.hide(byHeight: false)
            }
            else {
                viewMoreOrLess.hide(byHeight: true)
                viewLess.hide(byHeight: true)

            }

0

Це рішення допоможе розрахувати висоту і ширину під час виконання.

    let messageText = "Your Text String"
    let size = CGSize.init(width: 250, height: 1000)
    let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
    let estimateFrame = NSString(string: messageText).boundingRect(with:  size, options: options, attributes: [NSAttributedString.Key.font: UIFont(name: "HelveticaNeue", size: 17)!], context: nil)

Тут ви можете обчислити орієнтовну висоту, яку буде приймати ваша рядок, і передати її до кадру UILabel.

estimateFrame.Width
estimateFrame.Height 

0

Швидкий 5:

Якщо у вас є UILabel і якийсь спосіб limitingRect не працює для вас (я стикався з цією проблемою. Він завжди повертав висоту 1 рядка), є розширення, щоб легко обчислити розмір мітки.

extension UILabel {
    func getSize(constrainedWidth: CGFloat) -> CGSize {
        return systemLayoutSizeFitting(CGSize(width: constrainedWidth, height: UIView.layoutFittingCompressedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
    }
}

Ви можете використовувати його так:

let label = UILabel()
label.text = "My text\nIs\nAwesome"
let labelSize = label.getSize(constrainedWidth:200.0)

Працює для мене


-2
@IBOutlet weak var constraintTxtV: NSLayoutConstraint!
func TextViewDynamicallyIncreaseSize() {
    let contentSize = self.txtVDetails.sizeThatFits(self.txtVDetails.bounds.size)
    let higntcons = contentSize.height
    constraintTxtV.constant = higntcons
}

5
Ваша відповідь повинна складатися не лише з коду, але й з пояснення щодо коду. Будь ласка, див. Як відповісти для отримання більш детальної інформації.
MechMK1

Хоча цей код може відповісти на питання, надаючи додатковий контекст стосовно того, чому та / або як цей код відповідає на питання, покращує його довгострокове значення.
Ісма

Ця відповідь неповна. Він посилається на важливі змінні,
чиї
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.