tableView「もっと見る」の実装


「もっと見る」をタップしたらtableViewデータが全件表示されるようにしたい……
そんな時は、データを表示するセルとは別に「もっと見る」というセルを作ってあげれば解決です!
今回はtableViewに表示するデータの10件だけ初期表示しておいて、残りは「もっと見る」をタップされたら表示するといった実装をしてみます。

1. もっと見るを表示する用のカスタムセルを作成

カスタムセルの作り方は多数記事があるのでここでは割愛します。

2. 表示するセルの個数を設定

もっと見るが表示されているか否かを判定するflagを作成。
flagがtrueだったら、表示するセル数をもっと見る用に+1してあげます。

private var moreFlag: Bool = true
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return moreFlag ? filteredArray.count + 1 : filteredArray.count
    }

3. 10件だけ表示するために、データをfilterします。

filterしたデータを格納するための配列を作ります。そしてこの配列が格納されたらtableViewに表示させたいので、didSetにtableView.reloadData()を書きます。

private var filteredArray: [String] = [] {
        didSet {
            tableView.reloadData()
        }
    }

10件だけをfilterして先ほど作った配列、filteredArrayに格納します。

    private func updateDataSource() {
        filteredArray = array.enumerated().filter { $0.0 < 15 }.map { $0.1 }
    }

画面が表示された時点で、updateDataSource()が呼ばれる -> filteredArrayが格納される -> tableViewが更新されて画面に表示される といった流れを作りたいので、updateDataSource()はViewDidLoadで発火させておきましょう。

4. データを表示するかもっと見るを表示するかを判定する分岐を書きます。

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if indexPath.row < filteredArray.count {
            let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as! CustomTableViewCell
            cell.setUp(title: array[indexPath.row])
            return cell
        } else {
            let cell = tableView.dequeueReusableCell(withIdentifier: "MoreTableViewCell", for: indexPath) as! MoreTableViewCell
            return cell
        }
    }

ここまでで、一旦10件のデータと、もっと見るセルが表示されている状態だと思います。
最後はもっと見るを押した時の挙動を実装します。

5. もっと見るを押した時の動き

もっと見るを押した時に、必要な挙動は、もっと見るセルの非表示と、残りのデータの全件表示の2点です。

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if indexPath.row < filteredArray.count {
            // 通常のセルがタップされた時に起こしたい動作を書く
        } else {
            moreFlag = false
            updateDataSource()
        }
    }
}

moreFlagのBool値によって、filteredArrayに格納させるのは10件でfilterした値か、arrayそのままかを判断されるようにします。

    private func updateDataSource() {
        filteredArray = moreFlag ? array.enumerated().filter { $0.0 < 15 }.map { $0.1 } : array
    }

これで完成です!
実装したいものに合わせて少しでも参考になれば幸いです!

さいごに、コードの全体像を載せておきます。

class ViewController: UIViewController {

    @IBOutlet private var tableView: UITableView!

    private let array: [String] = [
        "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", "A11", "A12", "A13", "A14", "A15", "A16", "A17", "A18", "A19", "A20"
    ]

    private var filteredArray: [String] = [] {
        didSet {
            tableView.reloadData()
        }
    }

    private var moreFlag: Bool = true

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self

        tableView.register(UINib(nibName: "CustomTableViewCell", bundle: nil), forCellReuseIdentifier: "CustomTableViewCell")
        tableView.register(UINib(nibName: "MoreTableViewCell", bundle: nil), forCellReuseIdentifier: "MoreTableViewCell")

        updateDataSource()
    }

    private func updateDataSource() {
        filteredArray = moreFlag ? array.enumerated().filter { $0.0 < 10 }.map { $0.1 } : array
    }
}

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if indexPath.row < filteredArray.count {
            // 通常のセルがタップされた時に起こしたい動作を書く
        } else {
            moreFlag = false
            updateDataSource()
        }
    }
}

extension ViewController: UITableViewDataSource {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return moreFlag ? filteredArray.count + 1 : filteredArray.count
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 62
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if indexPath.row < filteredArray.count {
            let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath) as! CustomTableViewCell
            cell.setUp(title: array[indexPath.row])
            return cell
        } else {
            let cell = tableView.dequeueReusableCell(withIdentifier: "MoreTableViewCell", for: indexPath) as! MoreTableViewCell
            return cell
        }
    }
}