はじめに
以前書いたデフォルトTableViewサンプル集(swift)のテーブルカスタム編です。
アコーディオンテーブルをつくってみよう
※ タイトルに「つくろう!」って書いてるけどアコーディオンテーブルを推奨しているわけではないです
アコーディオンテーブルをつくる(1階層)
完成形はこんな感じ
色々やり方はあるかと思いますが今回はセクションヘッダーを使います。
やること
- セクションヘッダーのタップを検知する
- 開いているセクション情報を保持する
- 開閉時の表示処理をする
セクションヘッダーのタップを検知する
セクションヘッダーのタップ検知はどのセクションをタップしたかわかるようにする必要があるので section: Int
を保持させるためにカスタムの UITableViewHeaderFooterView
を作ります。
こんな感じ
protocol SingleAccordionTableViewHeaderFooterViewDelegate: AnyObject {
func singleAccordionTableViewHeaderFooterView(_ header: SingleAccordionTableViewHeaderFooterView, section: Int)
}
class SingleAccordionTableViewHeaderFooterView: UITableViewHeaderFooterView {
weak var delegate: SingleAccordionTableViewHeaderFooterViewDelegate?
@IBOutlet weak var nameLabel: UILabel!
var section = 0
override func awakeFromNib() {
contentView.backgroundColor = .systemBackground
}
@IBAction private func didTap(_ sender: Any) {
delegate?.singleAccordionTableViewHeaderFooterView(self, section: section)
}
}
xibはこんな感じ
タップ検知はジェスチャでもいいですがめんどくさいので View いっぱいに Button をのせています。
そして、ボタン押下時にデリゲートで通知しています。
テーブル実装側はこんな感じ
override func viewDidLoad() {
super.viewDidLoad()
// ヘッダーの登録
tableView.register(UINib(nibName: "SingleAccordionTableViewHeaderFooterView", bundle: nil), forHeaderFooterViewReuseIdentifier: "Header")
}
// UITableViewDelegate
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "Header") as! SingleAccordionTableViewHeaderFooterView
header.nameLabel.text = "テキスト"
header.section = section // セクション保持
header.delegate = self // デリゲート設定
return header
}
開いているセクション情報を保持する
テーブルを実装している ViewController でどこのセクションが開いていてどこが閉じているのかという情報を保持しないといけません。
こんな感じ
var expandSectionSet = Set<Int>() // こいつで開いているセクション保持
extension SingleAccordionTableViewController: SingleAccordionTableViewHeaderFooterViewDelegate {
// セクションタップ時に呼ばれる
func singleAccordionTableViewHeaderFooterView(_ header: SingleAccordionTableViewHeaderFooterView, section: Int) {
if expandSectionSet.contains(section) {
expandSectionSet.remove(section)
} else {
expandSectionSet.insert(section)
}
}
}
上記のようにカスタムセクションのデリゲートでセクションの開閉状態を保持します。
開閉時の表示処理をする
あとはセクションの開閉の表示処理をしてやるだけです。閉じてるセクションでは row の数を0にしてやればいい感じになります。
こんな感じ
// UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return expandSectionSet.contains(section) ? tableDataList[section].count : 0
}
さらに、セクションタップ時に指定のセクションのリロード処理を呼んであげます。
extension SingleAccordionTableViewController: SingleAccordionTableViewHeaderFooterViewDelegate {
// セクションタップ時に呼ばれる
func singleAccordionTableViewHeaderFooterView(_ header: SingleAccordionTableViewHeaderFooterView, section: Int) {
if expandSectionSet.contains(section) {
expandSectionSet.remove(section)
} else {
expandSectionSet.insert(section)
}
}
tableView.reloadSections([section], with: .automatic) // これ追加
}
これでいい感じにタップ時にアニメーションしながら開閉するようになりました!
完成!!
アコーディオンテーブルをつくる(2階層)
テーブルセルに工夫を加えることによりさらに多階層のアコーディオンも可能です。
ちからつきた...
そーすみてね ー> github
さいごに
アコーディオンテーブルをつくってみましたが、iOS でこういう階層構造にしたい場合は NavigationController で Push するのが正攻法かと思います。(標準のファイルアプリみたいに)
アコーディオンテーブルにしたいという要望は PC 版 (Windows) のやつの iOS 版つくりたい!みたいな業務系アプリで多い印象があります
テーブルのサンプル集のプルリク募集中!!(なんでもいいよなんか思いついたら上げてます)