Swift LUExpandableTableViewを使って折りたたみ可能なUITableViewを実装
こんな感じのありがちなUI。ライブラリを使わずに自前で実装する方法もけっこう書いていただいている人がいて実装することはできたのだが、テーブルのレコード数が増えると開閉したときのスクロール位置がガタガタ。
自分の実装方法に問題があるかもしれないが細かいところまで解決できずいたところにこちらのライブラリを見つけた。少し同じ問題は発生するのだが自分でやるよりは全然マシでこちらを使うことにしたので使い方をメモしておく。
https://github.com/LaurentiuUngur/LUExpandableTableView

ViewController.swift
import UIKit
import LUExpandableTableView
class ViewController: UIViewController, LUExpandableTableViewDataSource, LUExpandableTableViewDelegate {
var expandableTableView = LUExpandableTableView()
override func viewDidLoad() {
super.viewDidLoad()
expandableTableView.frame = view.bounds
view.addSubview(expandableTableView)
expandableTableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
expandableTableView.register(CustomHeader.self, forHeaderFooterViewReuseIdentifier: "CustomeHeader")
expandableTableView.expandableTableViewDelegate = self
expandableTableView.expandableTableViewDataSource = self
}
// MARK: - LUExpandableTableViewDelegate
// セルの高さを返します
func expandableTableView(_ expandableTableView: LUExpandableTableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
// セクションヘッダーの高さを返します
func expandableTableView(_ expandableTableView: LUExpandableTableView, heightForHeaderInSection section: Int) -> CGFloat {
let header = CustomHeader()
return header.setup(text: "セクションタイトル")
}
// MARK: - LUExpandableTableViewDataSource
// セクション数を返します
func numberOfSections(in expandableTableView: LUExpandableTableView) -> Int {
return 100
}
// セル数を返します
func expandableTableView(_ expandableTableView: LUExpandableTableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
// View(セル)を返します
func expandableTableView(_ expandableTableView: LUExpandableTableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = expandableTableView.dequeueReusableCell(withIdentifier: "Cell") else {
return UITableViewCell()
}
cell.textLabel?.text = "セル"
return cell
}
// View(セクション)を返します
func expandableTableView(_ expandableTableView: LUExpandableTableView, sectionHeaderOfSection section: Int) -> LUExpandableTableViewSectionHeader {
guard let header = expandableTableView.dequeueReusableHeaderFooterView(withIdentifier: "CustomeHeader") as? CustomHeader else {
return LUExpandableTableViewSectionHeader()
}
let _ = header.setup(text: "セクション")
return header
}
}CustomeHeader.swift
class CustomHeader: LUExpandableTableViewSectionHeader {
var label = UILabel()
var button = UIButton()
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var isExpanded: Bool {
didSet {
self.button.setTitle(isExpanded ? "閉じる" : "開く", for: .normal)
}
}
func setup(text: String) -> CGFloat {
backgroundView = UIImageView()
backgroundView?.backgroundColor = UIColor.init(red: 157/255, green: 204/255, blue: 224/255, alpha: 1)
label.frame = CGRect(x: 10, y: 0, width:UIScreen.main.bounds.size.width*0.8, height: 100)
label.text = text
label.font = UIFont.boldSystemFont(ofSize: 13)
addSubview(label)
button.frame = CGRect(x: label.frame.width, y: 0, width: UIScreen.main.bounds.size.width*0.2, height: 100)
button.setTitle(isExpanded ? "閉じる" : "開く", for: .normal)
button.setTitleColor(UIColor.black, for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 13)
button.addTarget(self, action: #selector(onOpen(sender:)), for: .touchUpInside)
addSubview(button)
return button.frame.origin.y + button.frame.height
}
@objc func onOpen(sender: UIButton) {
delegate?.expandableSectionHeader(self, shouldExpandOrCollapseAtSection: section)
}
}以上です