アコーディオンテーブルをつくろう！（swift）

# はじめに
以前書いた[デフォルトTableViewサンプル集(swift)](https://qiita.com/am10/items/9bbbe794e88a96e5420e)のテーブルカスタム編です。 
アコーディオンテーブルをつくってみよう:punch:

[ソース](https://github.com/adventam10/TableSample)

※ タイトルに「つくろう！」って書いてるけどアコーディオンテーブルを推奨しているわけではないです:rolling_eyes:

# アコーディオンテーブルをつくる（1階層）
完成形はこんな感じ

![single](https://user-images.githubusercontent.com/34936885/80277463-12158d80-872a-11ea-89b2-f648f411aef2.gif)

[ソース](https://github.com/adventam10/TableSample/tree/master/TableSample/TableSample/Tables/Customs/SingleAccordion)

色々やり方はあるかと思いますが今回はセクションヘッダーを使います。

やること 
1. セクションヘッダーのタップを検知する
2. 開いているセクション情報を保持する
3. 開閉時の表示処理をする

## セクションヘッダーのタップを検知する
セクションヘッダーのタップ検知はどのセクションをタップしたかわかるようにする必要があるので `section: Int` を保持させるためにカスタムの `UITableViewHeaderFooterView` を作ります。

こんな感じ

```swift
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はこんな感じ

<img width="500" alt="xib" src="https://user-images.githubusercontent.com/34936885/80277467-1cd02280-872a-11ea-99f1-53a202664e1f.png">

タップ検知はジェスチャでもいいですがめんどくさいので View いっぱいに Button をのせています。
そして、ボタン押下時にデリゲートで通知しています。

テーブル実装側はこんな感じ

```swift
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 でどこのセクションが開いていてどこが閉じているのかという情報を保持しないといけません。

こんな感じ

```swift
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にしてやればいい感じになります。

こんな感じ

```swift
// UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return expandSectionSet.contains(section) ? tableDataList[section].count : 0
}
```

さらに、セクションタップ時に指定のセクションのリロード処理を呼んであげます。

```swift
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) // これ追加
}
```

これでいい感じにタップ時にアニメーションしながら開閉するようになりました！ 
完成！！:clap:

# アコーディオンテーブルをつくる（2階層）
テーブルセルに工夫を加えることによりさらに多階層のアコーディオンも可能です。

![accordion](https://user-images.githubusercontent.com/34936885/80277474-2b1e3e80-872a-11ea-9f60-a965f889288d.gif)

ちからつきた...  
そーすみてね:kissing_heart: ー＞ [github](https://github.com/adventam10/TableSample/tree/master/TableSample/TableSample/Tables/Customs/DoubleAccordion)

# さいごに
アコーディオンテーブルをつくってみましたが、iOS でこういう階層構造にしたい場合は NavigationController で Push するのが正攻法かと思います。(標準のファイルアプリみたいに) 
アコーディオンテーブルにしたいという要望は PC 版 (Windows) のやつの iOS 版つくりたい！みたいな業務系アプリで多い印象があります:thinking:

テーブルのサンプル集の[プルリク募集中！！](https://github.com/adventam10/TableSample/blob/master/CONTRIBUTING.ja.md)（なんでもいいよ:rolling_eyes:なんか思いついたら上げてます）