noki雑記

iOS、ときどきAndroid

UITableView の reloadSections アニメーションの不具合

ヘッダーをタップすることでセルを開閉できる、アコーディオンのようなテーブルビューを作ったときの話です。ヘッダーだけ残した状態で、セルとフッターを隠したり表示したりを切り替えたくて、 reloadSections:withRowAnimation: で開閉を実装しました。ところが、とある条件下だと開閉時のアニメーションが期待通りに動作しないことがありました。

問題

ヘッダーだけ残し、セルとフッターが隠されている状態のときに、そのセクションの1つ下のセクションの開閉アニメーションが期待通りに動作していませんでした。隠したいセクションのセルの数を numberOfRowsInSection で0を返していたのですが、ここに問題があるような気がしています。セクションのヘッダーは表示されているのにセルが0の場合は、セクション毎非表示にするのが正しいのでしょうか。
期待しない動作というのも、言葉では言いづらいのですが、アニメーション(UITableViewRowAnimation.Automatic)の開始位置がずれてしまうというものです。セルの数が0のセクションの下にセルの数が1個以上のセクションがある場合、セルが1個以上のセクションの開閉アニメーションの開始位置が上のセクションのヘッダー部分となってしまっていました。

期待する動作

とりあえず、セクションが空であるかどうかに関わらず、reloadSections 実行時のセルの開閉アニメーションの開始位置が、開閉するセクションのヘッダー部分であることとします。

対応

うまい方法が思いつかなかったので、下記の方法で対処しました。 * numberOfRowsInSection で0を返していた箇所で1を返す * cellForRow(at:) でダミーとなるセルを返す * tableView(_:heightForRowAt:) で高さを CGFloat.min を返す

まとめ

そもそも insert​Rows(at:​with:​)delete​Rows(at:​with:​)を使用する手もあるのですが、今回はセルと一緒にフッターも開閉する必要があり、reloadSections をどうしても使いたかったという背景があります。が、こういう問題があるのはつらいなぁと思いました。