Blog スタッフブログ

iOS Swift システム開発

[Swift]複数のUITableViewのスクロールを同期させる

Swift

こんにちは、株式会社MIXシステム開発担当のBloomです。

今回は複数のUITableViewやUIScrollViewでスクロールを同期させたい場合について、お仕事の中で得た知見を共有させていただきます。

UIScrollViewDelegate

extension ViewController: UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate {
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 60
    }
    func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
        return 0
    }
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        syncronizeScroll(scrollView: scrollView)
    }
    private func syncronizeScroll(scrollView: UIScrollView) {
        guard !isSyncing else { return }
        isSyncing = true
        alignOffsets(scrollView)
        isSyncing = false
    }

    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        alignOffsets(scrollView)
    }
    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        if !decelerate { alignOffsets(scrollView) }
    }
    private func alignOffsets(_ scrollView: UIScrollView) {
        let y = scrollView.contentOffset.y
        self.tableView1.contentOffset.y = y
        self.tableView2.contentOffset.y = y
    }

}

syncronizeScroll関数内において、ここでは再帰的な呼び出しを防止するためにisSyncing変数を別途設置することで処理のループを防いでいます。

また、この関数内でsetContentOffset(_:animated:)を利用してしまうと慣性が消失してしまい使用感が悪化するため注意しましょう。

scrollViewDidEndDeceleratingにおいて慣性スクロールの終了時にまたスクロール位置の同期を行なっています。これはheightForRowAtやestimatedHeightForRowAtにおいての明示的な指定と関連しており、稀にスクロール位置がズレて同期されてしまうことへの対策になります。

これだけでふたつのUITableViewのスクロール位置を同期することができます。良かったですね。