Swift

【swift入門】TableViewCellの高さを可変にする

はじめに

前回Xibファイルのレイアウトを設定しようTableViewCellのレイアウトを設定しました。
その際に生じた問題点を解決するために、TableViewCellの高さを可変にするということをやっていきます。

現状のレイアウト

現状のレイアウトはこのようになっています。

上のタイトルのLabelは高さが渡ってくる文字列の長さに応じて可変、それ以外は高さが固定値で定義されています。

現状ではセルの高さを固定しているので流れとしては、

  1. セルの高さが与えられて決定する
  2. オートレイアウトで固定値で設定してるものはその値で決定
  3. オートレイアウトで固定値で設定していないものが計算で高さが決まる

というようになっているので、タイトルの長さがどうであろうとセルの高さに依存して全て同じようになってしまいます。

そこで今回は、

  1. タイトルの長さに合わせてLabelの高さが決まる
  2. オートレイアウトで固定値で設定してるものはその値で決定
  3. セルの高さが決まる

という流れになるようにしていきたいです。

設定のやりかた

このための機能はUITableViewに用意されていて、その設定をするだけです。

以下のようにしました。


import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!

    fileprivate var articles: [Article] = [] {
        didSet {
            tableView.reloadData()
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        fetchArticles()
        initTableView()
    }

    private func fetchArticles() {
        guard let url: URL = URL(string: "http://qiita.com/api/v2/items") else { return }
        let task: URLSessionTask  = URLSession.shared.dataTask(with: url, completionHandler: {data, response, error in
            do {
                guard let data = data else { return }
                guard let jsonArray = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [Any] else { return }
                let articlesJson = jsonArray.flatMap { (json: Any) -> [String: Any]? in
                    return json as? [String: Any]
                }
                let articles = articlesJson.map { (articleJson: [String: Any]) -> Article in
                    return Article(json: articleJson)
                }
                DispatchQueue.main.async() { () -> Void in
                    self.articles = articles
                }
            }
            catch {
                print(error)
            }
        })
        task.resume()
    }

    private func initTableView() {
        tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "TableViewCell")
        tableView.estimatedRowHeight = 150 //追加
        tableView.rowHeight = UITableViewAutomaticDimension //追加
    }
 

extension ViewController: UITableViewDataSource {
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return articles.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: TableViewCell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as! TableViewCell
        let article = articles[indexPath.row]
        cell.bindData(article: article)
        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableViewAutomaticDimension //変更
    }
}

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("section: \(indexPath.section) index: \(indexPath.row)")
    }

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        return
    }
}

変更追加したのは3行だけです
まず、initTableViewに以下の2行を追加しました。


private func initTableView() {
    tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "TableViewCell")
    tableView.estimatedRowHeight = 150 //追加
    tableView.rowHeight = UITableViewAutomaticDimension //追加
}

ここで設定するestimatedRowHeightは仮置きのセルの高さです。一旦仮置きの高さで計算してからセルの高さを実際に計算するのでここで設定する数値は実際の数値に近づけるほど処理にかかる時間が早くなります。

そして、heightForRowAtの部分で今まで固定値を書いていましたがこのように書き換えます。


func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return UITableViewAutomaticDimension //変更
}

ビルドしてみる

先程のように設定してビルドしてみるとこのようになりました。

うまくタイトルが表示されています。

今回のコードはこちらでも確認できるのでよかったらご確認ください。