LoginSignup
30
19

More than 5 years have passed since last update.

UILabelの複数行可変高さまとめ

Posted at

何かと面倒なiOSの高さ問題。AutoLayoutを使う前提です。

共通

コードからでもStoryboardからでも構わないですが、UILabelのnumberOfLinesを0にします。
これで複数行表示できるようになります。

UITableViewCellの中で使う場合

Cellの高さを自動で計算させる方法があります。
SwiftでUITableViewCellの高さを動的に変更する

ポイントは、
1. UILabelと隣接するViewまでの上下距離に関する制約をつける
2. 以下の2行をviewDidLoad()あたりに書く

tableView.estimatedRowHeight = 適当な高さ
tableView.rowHeight = UITableViewAutomaticDimension

後は普通にUITableViewのDelegateとDataSourceを実装すればOK。
高さは勝手に計算されるので、heightForRowAtは不要です。

なんかスクロールがおかしい・・・?

上記だけでは、reloadData()とかscrollToRowAtIndexPath()をするとスクロールがおかしな位置に行ってしまうことがあります。
まだ生成されていないセルの高さを、estimatedRowHeightで設定した値で計算するためみたいです。
Autolayout で UITableViewCell の高さを設定すると reloadData 後にカクカクすることがある

正常にスクロールさせるには、estimatedHeightForRowAtで正確な高さを返します。
とは言え一度セルが生成されるまでは正確な高さはわからないので、最初はestimatedRowHeightを返します。
そして、willDisplayなどセルが生成された後で、実際のセルの高さを配列などに保存し、次からは保存した高さを返します。

これらを追加
private var cellHeightList: [IndexPath: CGFloat] = [:]

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    if let height = self.cellHeightList[indexPath] {
        return height
    }
    else {
        return tableView.estimatedRowHeight
    }
}

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if !self.cellHeightList.keys.contains(indexPath) {
        self.cellHeightList[indexPath] = cell.frame.size.height
    }
}

UIViewで使う場合

子供の要素から適切なサイズを計算してくれる素敵なメソッドがあります。
UIKit - AutoLayoutを利用した高さ可変のTableHeaderViewをつくる

ポイントは、
1. UILabelと隣接するViewまでの上下距離に関する制約をつける
2. UILabelのtextに文字列を入れる
3. UIViewに対して、setNeedsLayout()とlayoutIfNeeded()を実行
4. systemLayoutSizeFitting(UILayoutFittingCompressedSize)でサイズを計算
5. 4で得られたサイズを使ってUIViewのframeなり制約なりを更新する

例えば高さの制約を更新する場合
@IBOutlet weak var someViewHeightConstraint: NSLayoutConstraint!
private var someView: UIView!
private var label: UILabel!
func calcHeight() {
    // 手順2
    label.text = "複数行のテキスト"
    // 手順3
    someView.setNeedsLayout()
    someView.layoutIfNeeded()
    // 手順4
    let size = someView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
    // 手順5
    someViewHeightConstraint.constant = size.height
}

これって・・・

そうですね、セルの高さ計算にも使えます。
テーブルビューのセル毎にピッタリの高さを計算する
でも最初に紹介した方法のほうが簡単です。

おわりに

昔は頑張って計算するしかなかったみたいなので、それに比べればだいぶ楽になりました。
それでもAutoLayoutとかテーブルの高さ計算の仕組みとかわかってないとうまくいかなかったりするので、メモ代わりに記事にしました。
リンク集みたいになってしまいましたが、誰かの役にたてば幸いです。

30
19
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
30
19