#まずはじめに
UITableViewの下部に空間が欲しいと思ったことはないでしょうか。
その理由としてボタンやウインドウが被る。しかしfooterを設定するとタップ判定が無くなる。
Tableの高さを変更する処理にするとCellが一瞬消滅して見える。。などなど
簡単そうに見えて意外と容易くないことが今回調べていてわかったので4つの処理パターンに分けてまとめました。
#検証するUITableViewの例
[ Version 11.1 Swift5.1 ]
例えば下の画像のようなUITableViewを実装したとします。
右下のボタンをタップするとモーダルウィンドウが上がってきます。
問題
このままだと少し問題があります。
モーダルが一番下のCell約2つ分に被ってしまい、モーダルが出ている間は2つ分が見えなくなってしまい、UI的に優しくありません。
処理1:TableViewをキーボードの高さ分上げる
//キーボード表示時画面を持ち上げる
NotificationCenter.default.addObserver(self, selector: #selector(NewTaskViewController.keyboardWillShow(_ :)), name: UIResponder.keyboardWillShowNotification, object: nil)
//キーボード閉じる時に画面を下げる
NotificationCenter.default.addObserver(self, selector: #selector(NewTaskViewController.keyboardWillHide(_ :)), name: UIResponder.keyboardWillHideNotification, object: nil)
###処理1の問題点
スクロールポジションを任意に設定したい場合に限りますが、
tableの天井部分が画面外に出ているため、
モーダルが出ている間は適切なスクロールポジションに移動しない。
##処理2:footerを設ける
//フッターの色を透明に
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footerView: UIView = UIView()
footerView.backgroundColor = UIColor.clear
return footerView
}
//フッターの高さ
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return 400 //高さ
}
すると下のように最大スクロール時footerの高さ分余白ができるためモーダル出現時であってもCellを全て確認できます。
処理2の問題点
それはfooter範囲内はCellが存在している場合でもタップやスワイプが認識しないという点です。
バグか仕様か判断つかないですし、ユーザーに明らかに優しくありません。
処理3:UITableViewの高さを変更する
ボタンタップ時の処理として以下のように実装すると良いでしょう。
UIView.animate(withDuration: 0.5, delay: 0.5, options: .オプション, animations: {
//tableの高さを変更
self.tableView.fs_height = self.tableHeight - 400 //高さを縮小
}, completion: nil)
今回delay(アニメーション発生遅延)を設定しています。
理由は次で下でお伝えします。
処理3の問題点
こちらの問題。ボタンをタップした場合、高さ変更のアニメーション”開始時”にCellが消えて見えます。
(上のコードのようにdelayを設定してもCellの消えるタイミングは遅れません。)
機能には問題ないですし、閉じた後にスクロールして消えたCellを表示することはできますが、ユーザー目線に立つと気持ちが悪いです。
処理4:最後尾に透明なCellを追加して高さを設定する
この方法は少し煩雑化しますが、モーダルを閉じた状態でも、最大スクロールした時に下に空白を自由に設定でき、同様に擬似headerも作れそうです。
####CellForRowAt内で
if array[indexPath.row] == "footerKey"{
//footerはモーダルと同じ高さを設定
footerIndexPath = indexPath.row
tableView.rowHeight = 400
}
WillDisplayForRowAt内で
//Cell内アイテムにtagを1〜3設定していた場合の例
if array[indexPath.row] == "footerKey" {
for i in 1...3{//Cellがfooterの場合は非表示
(cell.contentView.viewWithTag(i))!.isHidden = true
}
上記以外にfooterは透明なCellであることから、スワイプおよびタップの認識を無視するコードを書く必要があります。
(こちらは別記事にまとめたいと思います。)
処理4の問題点
処理が煩雑化します。配列にダミーのデータを入れて、要素をClearにして擬似的にスペースを作っているためです。
しかし、処理1,2,3のような問題点が無いため、私は処理4を採用いたしました。
19.11.18追記(カクツキ問題の解決)
処理4でfooterCellの高さを
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
内で動的に変更していた場合、画面遷移直後下から上へスクロール時
少しカクツキが発生します。
(スクロール開始直後に高さが描写されるからだと思われます。)
footerのCellを他のCellと差異を作らず、同一にし、必要分だけダミーのfooterCellを作成すれば
この欠点は解決いたしました!
#まとめ
記事は以上となります!
私はSwiftを学習し始めて50日です。現在初の自作アプリリリースに向けて開発を進めているのですが、インプットやアウトプットもまだまだ足りておらず、日々実装しながら検証の繰り返しです。
Qiitaに記事が無かった物に関しては積極的に記事にして行きたいと思っています!
ご覧いただきありがとうございました。
初学者の方々にお役に立てたら幸いです。