Swiftの文字列に基づいてUILabelのサイズを把握する


183

さまざまな文字列の長さに基づいて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

}

上記は、私が高さを決定するために使用する現在の関数ですが、機能していません。私が得ることができるどんな助けも大いに感謝します。Objective Cではなく、Swiftで答えを推測します。


重複してみてくださいこのstackoverflow.com/a/61887135/6314955
Malith Kuruwita

回答:


518

拡張子を使用する 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メソッドの編集をチェックします。
Kaan Dedeoglu、2015年

7
@KaanDedeoglu "numberOfLines" = 0(UILabel固有である可能性がありますが不明)またはlineBreakMode ByWordWrappingを使用する場合のように、これは動的高さ文字列でどのように機能しますか。私の推測では、これをこのような属性に追加することでしたが、機能[NSFontAttributeName: font, NSLineBreakMode: .ByWordWrapping]しませんでした
Francisc0

1
私の答えがわかったと思う。私NSParagraphStyleAttributeName : styleはスタイルがNSMutableParagraphStyleである場所を使用する必要があります
Francisc0

4
「boundingRect」の代わりに「self.boundingRect」を記述する必要があります。そうしないと、コンパイルエラーが発生します。
Mike M

1
この答えをsizeForItemAtIndexPathaで使用したときに見つけた1つのことUICollectionViewは、その戻り値をオーバーライドするように見えることですinsetForSectionAt
Zack Shapiro

15

複数行のテキストの場合、この回答は正しく機能しません。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では、図面を何度も通過するため、100万のパフォーマンスの問題が発生します。リソースのサイズを手動で計算する方が
はるかに

2
このソリューションでは、UILabelのUIFontを設定して、正しい高さを確保する必要があります。
Matthew Spencer

私にとっては完璧に動作します-(受け入れられた回答とは異なり)空の文字列も考慮します。自動高さセルを使用してtableViewの高さを計算するのに非常に便利です。
ヘンディ

受け入れられた回答は固定幅で機能しましたが、固定高さでは機能しませんでした。高さが固定されている場合は、テキストに改行がない限り、1行にすべてが収まるように幅が増加します。これが私の別の回答です。私の回答
MSimic

2
私は同様の解決策を投稿しましたsizeToFit
ません

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

受け入れられた回答は、固定幅では機能するが、高さでは機能しないことがわかりました。高さが固定されている場合は、テキストに改行がない限り、1行にすべてが収まるように幅が増加します。

width関数はheight関数を複数回呼び出しますが、これは簡単な計算であり、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
Vyachaslav Gerchicov

これは、関数内の計算のシード値にすぎません。幅が大きいと予想される場合、最小のminimumTextWrapWidthを選択すると、whileループが追加の反復を実行します。したがって、最小幅は大きいほど良いですが、実際に必要な幅よりも大きい場合は、常に返される幅になります。
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を使用していて、boundingRectが機能しない場合(私はこの問題に直面しました。常に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 2017年

このコードは質問に答えることがありますが、このコードが質問に答える理由や方法に関する追加のコンテキストを提供すると、長期的な価値が向上します。
Isma 2017年

この答えは不完全です。それはタイプを知らない重要な変数を参照しており、目的を
達成できません
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.