LoginSignup
58
64

More than 5 years have passed since last update.

セクション単位で折りたためる、アコーディオン効果付きのUITableViewの作り方

Last updated at Posted at 2015-10-25

セクション単位で折りたたむことのできるUITableViewを、ネットの情報をいろいろ調べながら作成していたのですが、どれもいろいろ気になるところがあって自分なりの実装をしてみました。気になるところもあり完成版ではないのですが、iOSの得意な人に指摘してもらえれば嬉しいです。

完成イメージ

ExpandTableView.gif

ヘッダーのタップイベント取得

UITableViewは、ヘッダー部分をタップしたときのイベントを取得する機能がありません。このイベントを取得するために、ネットでは次の方法を見つけることができました。

しかしどれも無理矢理感がありしっくりこなかったので、ヘッダー位置のセルをカスタムで作成してGestureを登録するようにしてみた。

ViewController.swift
    override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        var cell = tableView.dequeueReusableHeaderFooterViewWithIdentifier("Header") as? CustomHeaderFooterView
        if cell == nil {
            cell = CustomHeaderFooterView(reuseIdentifier: "Header")
            cell?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "tapHeader:"))
        }
        cell!.textLabel!.text = self.sections[section].title
        cell!.section = section
        cell!.setExpanded(self.sections[section].extended)
        return cell
    }

    func tapHeader(gestureRecognizer: UITapGestureRecognizer) {
        guard let cell = gestureRecognizer.view as? CustomHeaderFooterView else {
            return
        }
        let extended = self.sections[cell.section].extended
        self.sections[cell.section].extended = !extended
        tableView.reloadData()  // 追記で、reloadSectionsに変更しています
    }
CustomHeaderFooterView.swift
import UIKit

class CustomHeaderFooterView: UITableViewHeaderFooterView {
    private var arrow = UIImageView()
    var section: Int = 0

    override init(reuseIdentifier: String?) {
        super.init(reuseIdentifier: reuseIdentifier)
        self.arrow.translatesAutoresizingMaskIntoConstraints = false
        self.contentView.addSubview(self.arrow)
        self.contentView.addConstraints([
            NSLayoutConstraint(item: arrow, attribute: .Trailing, relatedBy: .Equal, toItem: self.contentView, attribute: .Trailing, multiplier: 1.0, constant: -8),
            NSLayoutConstraint(item: arrow, attribute: .CenterY, relatedBy: .Equal, toItem: self.contentView, attribute: .CenterY, multiplier: 1.0, constant: 0)])
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func setExpanded(expanded: Bool) {
        arrow.image = UIImage(named: expanded ? "ArrowUp" : "ArrowDown")
    }
}

セルの表示非表示

ヘッダーのイベント発生時にセルの表示非表示を制御するために、データソースからデータを削除する方法で実装しているものがありました。しかし、表示のためにデータソースそのものを編集するのはどうかなあと思ったので、tableView:numberOfRowsInSection:メソッドで、折りたたんでいるときには0件を返すようにしてみました

ViewController.swift
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
        return self.sections[section].extended ? sections[section].details.count : 0
    }

気になっているところ

  • セクションをタップしたときに、スクロール位置がずれることがある
  • アニメーション効果が入っていない
  • セクションをタップしたときに`tableView.reloadData()'で全レコードを更新している

追記しました

追記(アニメーション効果とtableView.reloadDataについて)

tableView.reloadSectionsを使うと、いい感じにアニメーションがついて、なおかつ対象となるセクションのみreloadするようになりました。

ViewController.swift
    func tapHeader(gestureRecognizer: UITapGestureRecognizer) {
        guard let cell = gestureRecognizer.view as? CustomHeaderFooterView else {
            return
        }
        let extended = self.sections[cell.section].extended
        self.sections[cell.section].extended = !extended
        tableView.reloadSections(NSIndexSet(index: cell.section), withRowAnimation: .None)
    }

ソースはこちらです。
https://github.com/nakaken0629/ExpandTableViewSample

58
64
0

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
58
64