19
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

and factoryAdvent Calendar 2018

Day 5

あえてAutoLayoutを使わずにUILabelの高さを計算する

Posted at

iOSエンジニアにとって高さの計算は常に大きな関心の対象でした🔍
AutoLayoutの出現によって高さの自動計算ができる手段が提供されましたが、パフォーマンスの問題や宗教的な理由でAutoLayoutを避ける場合もあります:shipit:
そんな時のために、AutoLayoutを使わないUILabelの高さの計算方法を紹介します💡

文字列の高さの計算

タイトルはUILabelの高さとしましたが、実際に計算するのは文字列の高さになります。
String(NSString)にはboundingRectという文字列の描画領域を計算するメソッドが存在します。
ドキュメント

boundingRect
func boundingRect(with size: CGSize, 
          options: NSStringDrawingOptions = [], 
       attributes: [NSAttributedString.Key : Any]? = nil, 
          context: NSStringDrawingContext?) -> CGRect

表示させたい幅は決まっていると思うので、第一引数sizeには幅を指定したサイズを渡します。高さはとりあえずとても大きい数値にします。
第二引数optionsには文字描画オプションを渡します。複数行の高さの計算を正確にするには.usesLineFragmentOriginを渡せとドキュメントに書いてあります。フォントによっては.usesFontLeadingも渡さないと高さが小さく計算されてしまうので、指定しておくのが無難です。
第三引数attributesには文字列の属性を渡します。フォントや行間を指定します。
第四引数contextには文字列描画の環境のようなものを渡します。nilを渡せばデフォルトのものが使われます。
戻り値は描画領域がですが、小数値になっているので、ceil()を使って整数にしてやります。

今回の目的はUILabelの高さを計算することなので、幅と文字列の属性はUILabelから取得します。文字列のサイズ計算の際は、外から渡ってくる値を使用できるようにします。

以上をまとめて、Stringのextensionを作ります。

文字列の高さを計算するためのメソッド
extension String {
    
    func makeSize(width: CGFloat, attributes: [NSAttributedString.Key: Any]) -> CGSize {
        let bounds = CGSize(width: width, height: .greatestFiniteMagnitude)
        let options: NSStringDrawingOptions = [.usesLineFragmentOrigin, .usesFontLeading]
        let rect = self.boundingRect(with: bounds, options: options, attributes: attributes, context: nil)
        let size = CGSize(width: rect.size.width, height: ceil(rect.size.height))
        return size
    }
}

UILabelの高さの計算

こちらは自身の幅と属性を上記のメソッドに渡してやるだけです。
プレーンテキスト用と属性付きテキスト用のメソッドをそれぞれ用意します。
それらをUILabelのextensionとして定義します。

UILabelの高さを計算するためのメソッド
extension UILabel {
    
    func getTextHeight() -> CGFloat {
        guard let text = self.text else {
            return 0.0
        }
        let attributes: [NSAttributedString.Key: Any] = [.font : self.font]
        let size = text.makeSize(width: self.bounds.width, attributes: attributes)
        return size.height
    }
    
    func getAttributedTextHeight() -> CGFloat {
        guard let attributedText = self.attributedText else {
            return 0.0
        }
        var attributes: [NSAttributedString.Key: Any] = attributedText.attributes(at: 0, effectiveRange: nil)
        attributes[.font] = self.font
        let size = attributedText.string.makeSize(width: self.bounds.width, attributes: attributes)
        return size.height
    }
}

これでUILabelの高さの計算ができるようになりました!

終わりに

ここまで書いて思い出したんですが、sizeThatFits()というメソッドがありましたね・・・🙀
まあそっちはNSStringDrawingOptionsの指定とかできないし、もしうまくいかないという場合は上記の方法を試してみてください👊

19
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?