LoginSignup
28

More than 3 years have passed since last update.

アコーディオンテーブルをつくろう!(swift)

Last updated at Posted at 2020-04-25

はじめに

以前書いたデフォルトTableViewサンプル集(swift)のテーブルカスタム編です。
アコーディオンテーブルをつくってみよう:punch:

ソース

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

アコーディオンテーブルをつくる(1階層)

完成形はこんな感じ

single

ソース

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

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

セクションヘッダーのタップを検知する

セクションヘッダーのタップ検知はどのセクションをタップしたかわかるようにする必要があるので 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はこんな感じ

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

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

アコーディオンテーブルをつくる(2階層)

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

accordion

ちからつきた...

そーすみてね:kissing_heart: ー> github

さいごに

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

テーブルのサンプル集のプルリク募集中!!(なんでもいいよ:rolling_eyes:なんか思いついたら上げてます)

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28