iOS15のUITableViewでは、ついに「空セルが表示されない」挙動がデフォルトになりました。以前は空セル表示がデフォルトだったため、それを消すために
tableView.tableFooterView = UIView(frame: .zero)
のようなワークアラウンド実装を行なった開発者の方は多いのではないかと思います。
しかし、iOS15の挙動変更はtableFooterViewのデフォルト値が空ビューになったのではなく、空セルの挙動が新しいfillerRowHeight(filter: フィルター、ではなく、filler: フィラー)というプロパティによって管理されるようになり、そのデフォルト値が「空セルを表示しない」値になっている事により実現されています。
この記事ではドキュメントに記載のある「プロパティの設定による挙動の違い3パターン」をサンプルとともに解説します。
なお、ここまでは馴染みのある「空セル」と呼称しましたが、これ以降は公式ドキュメントの表現に従って「filler row」という名称を使っていきます。「空いている部分を埋める行」というニュアンスです。
fillerRowHeightの3種類の挙動
前提として、**fillerRowはUITableView.styleが.plainの時でないと表示されません。**なので、スタイルがUITableView.Style.groupedやUITableView.Style.insetGroupedの場合にはfillerRowHeightに何の値を入れても無視されることになります。
そのため、fillerRowHeight
の設定を有効にしたい場合は、Storyboardの設定やコード初期化でstyle: .plain
を渡すようにします。
その上で、filterRowHeight
はその値によって大きく3種類の挙動をすることがドキュメントによって示されています。
・0.0を設定するとfiller rowsを表示しません。この挙動はiOS15以上ではデフォルトです。
・automaticDimensionを設定すると、table viewの最後の行の高さに合わせて、automatic heightを使うfiller rowsを表示します。この挙動はiOS15未満でデフォルトです。
・任意の正の値を設定すると、指定した高さのfiller rowsを表示します。
Apple inc., fillerRowHeight, Discussion, https://developer.apple.com/documentation/uikit/uitableview/3801921-fillerrowheight, viewed: 2022/02/19
引用部分の一行目と二行目見てもらうと分かりますが、デフォルト挙動が切り替わっていることも明示されています。
検証
上記の三種類の挙動を実際に動かしてみていきます。検証コードはPlaygroundで書かれていますので、お手元の環境でペーストしてお試しください。
fillerRowHeight.playground
//: A UIKit based Playground for presenting user interface
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController, UITableViewDataSource {
/*
style: .plainは必須
*/
let tableView = UITableView(frame: .zero, style: .plain)
override func loadView() {
let view = UIView()
view.backgroundColor = .white
view.addSubview(tableView)
// AutolayoutをサボりたいときにたまにresizingMaskを書きます
tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.dataSource = self
self.view = view
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
var contentConfiguration = cell.defaultContentConfiguration()
contentConfiguration.text = indexPath.row.description
cell.contentConfiguration = contentConfiguration
return cell
}
}
let myViewController = MyViewController()
// fillerRowHeight = 0.0
// 検証に必要な際にアンコメントしてください
//myViewController.tableView.fillerRowHeight = 0.0 // default
// fillerRowHeight = UITableView.automaticDimenssion
// 検証に必要な際にアンコメントしてください
//myViewController.tableView.fillerRowHeight = UITableView.automaticDimension
// fillerRowHeight = 任意の正の値
// 検証に必要な際にアンコメントしてください
myViewController.tableView.fillerRowHeight = 30
PlaygroundPage.current.liveView = myViewController
fillerRowHeight = 0.0
iOS15以上のデフォルト挙動である値ですが、filler rows
が表示されていません。
(スクリーンショットでは画像境界の識別性のためにborderの設定をしています)
fillerRowHeight = UITableView.automaticDimenssion
iOS15未満のデフォルト挙動である値ですが、filler rows
がテーブルの高さと同じだけ表示されています。行の高さは自動で決まっています。
fillerRowHeight = 任意の正の値
ここでは値を30
としました。
「filler rows
が指定した高さ分続く」のではなく「filler rows
はテーブルの高さと同じだけ表示されるが、1つ1つの行の高さを任意に変更できる」という振る舞いです。
まとめ
この記事では、我々がこれまでempty rows
, empty cells
などと読んでいたUITableView.Style.plainの際に表示されるあれをiOS15以降ではfillerRowHeight
というプロパティで設定できるようになったことを解説しました。
また、fillerRowHeight
に設定したCGFloat
の値によって実際にどうUITableViewの振る舞いが変わるのかをサンプルコードとスクリーンショットを使って例示しました。
参考
fillerRowHeight
https://developer.apple.com/documentation/uikit/uitableview/3801921-fillerrowheight
Apple Developer Forumでの質問
https://developer.apple.com/forums/thread/691284