UITableViewCellの高さを可変にする

UITableViewCellの高さを可変にする

Auto Layoutを活用してUITableViewCellの高さを可変にするやり方です。

参考:[iOS] Auto Layout を使いこなす。UITableViewCell と UIScrollView 編

UITableViewCellはAutoLayoutを設定していても、コンテンツの高さにあわせて変化してくれません。そこで、UITableViewCellの高さを可変にするには、少々手間を加える必要があります。

UITableViewCellの高さが子のUILabelなどの高さにあわさってくれないのは、Auto Layout計算を行うviewDidLayoutSubviewsが、コンテンツを設定するcellForRowAtIndexPathの呼び出しよりも先に処理されてしまうことによるようです。 そこで、この順序を変えてやればなんとかなるだろう、との読みが働きます。 つまり、viewDidLayoutSubviews中、とくにCellの高さを求めているUITableViewDelegate#heightForRowAtIndexPathの中からUITableViewDataSource#cellForRowAtIndexPathを呼んでやればうまくいくはずです。

もっとも、UITableViewCellの中に用意されているContentViewは、他のUIViewと違って中のUILabelなどのコンテンツの大きさにあわせて変わったりすることがないようです。 そこで、ContentViewの中にさらに一個UIViewを用意して、その中にコンテンツを用意することで対応する必要があります。 このとき、ラッパーになるUIViewのサイズが自動であわさるように適切に制約を設定する必要があります。 一例として、以下の様な設定例がありえます。

  • UIViewのTop, Trailing, Leadingを親に対して0にする
  • UIView内部で一番bottomに近い位置に置かれるコンテンツのbottom制約を親(UIView)に対して設定する
  • 上記bottom制約のpriorityを可変コンテンツの推奨Heightが決まるpriority(Content Hugging Priority)よりも低くする(Lowを選択すれば、通常はContent Hugging Priorityよりも低くなる)

このようにUIを配置した上で、カスタムしたTableViewCellに以下のような処理を加えます。

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    let cell = self.tableView(tableView, cellForRowAtIndexPath: indexPath) as CustomTableViewCell
    var newBounds = cell.bounds
    newBounds.size.width = tableView.bounds.width
    cell.bounds = newBounds

    cell.setNeedsLayout()
    cell.layoutIfNeeded()
    return cell.preferredView.bounds.height
}

※ここでCustomTableViewCellはカスタムしたTableViewCellのクラス名、preferredViewはラッパーになるUIViewの名前です。

これで、TableViewCellのサイズが適切に設定されるようになります。

(パフォーマンスの検証については未確認)

参考