LoginSignup
23
25

More than 5 years have passed since last update.

縦横両方向にスクロールできるビューを作ってみる

Last updated at Posted at 2016-09-10

はじめに

縦と横の両方向にスクロールしたいときありますよね?
そんなときに、TableViewCellにCollectionViewを設置することで実現できました。

完成イメージ

output.gif

実装サンプル

実装手順は、下記のとおりです。

  1. レイアウトを作成する
  2. TableViewCellのカスタムクラスを定義する
  3. TableViewCell中に設置するCollectionViewのCellのカスタムクラスを定義する
  4. 利用の仕方

1. レイアウトを作成する

レイアウトは、下記のような階層構造です。
ポイントは、TableViewCell内にCollectionViewを設置している点です。

スクリーンショット 2016-09-08 10.41.22.png

クラス名 説明
TableViewInCollectionViewCell TableViewCellのカスタムクラス名
CollectionViewCell TableViewCell中に設置するCollectionViewCellのカスタムクラス名
ViewController 画面クラス名

なお、CollectionViewのScroll Directionは、
横にスクロールするために「Horizontal」を指定しています。

2. TableViewCellのカスタムクラスを定義する

TableViewCell内で横スクロールできるようにしておきます。
また、何行目が呼ばれたか分かるようにtagに行番号を保存しておきます。

TableViewInCollectionViewCell.swift
import UIKit

class TableViewInCollectionViewCell: UITableViewCell {

    @IBOutlet weak var collectionView: UICollectionView!

    static var identifier: String {
        get {
             return String(self)
        }
    }
}

extension TableViewInCollectionViewCell {

    func setCollectionViewDataSourceDelegate<D: protocol<UICollectionViewDataSource, UICollectionViewDelegate>>(
        dataSourceDelegate: D, forRow row: Int) {

        collectionView.delegate = dataSourceDelegate
        collectionView.dataSource = dataSourceDelegate
        collectionView.tag = row
        collectionView.setContentOffset(collectionView.contentOffset, animated: false)
        collectionView.reloadData()
    }

    var collectionViewOffset: CGFloat {
        set {
            collectionView.contentOffset.x = newValue
        }

        get {
            return collectionView.contentOffset.x
        }
    }
}

3. TableViewCell中に設置するCollectionViewのCellのカスタムクラスを定義する

画像を表示するだけのセルを定義します。
自由にカスタマイズしてください。

CollectionViewCell
import UIKit

class CollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var imageView: UIImageView!

    static var identifier: String {
        get {
            return String(self)
        }
    }
}

4. 利用の仕方

ポイントは、TableViewを表示したタイミングでCollectionViewの表示及び、スクロールの調整をしている点です。

ViewController.swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    let dataSource = ViewController.generateRandomData() //テストデータ
    var offsets = [Int: CGFloat]()
}

extension ViewController: UITableViewDataSource {

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataSource.count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCellWithIdentifier(TableViewInCollectionViewCell.identifier,
                                                               forIndexPath: indexPath)
        return cell
    }
}

extension ViewController: UITableViewDelegate {

    //TableViewCellが表示されたときに、CollectionViewの描画する
    func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell,
                   forRowAtIndexPath indexPath: NSIndexPath) {

        guard let tableViewCell = cell as? TableViewInCollectionViewCell else {
            return
        }

        tableViewCell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row)
        tableViewCell.collectionViewOffset = offsets[indexPath.row] ?? 0
    }

    func tableView(tableView: UITableView,
                   didEndDisplayingCell cell: UITableViewCell,
                   forRowAtIndexPath indexPath: NSIndexPath) {

        guard let tableViewCell = cell as? TableViewInCollectionViewCell else {
            return
        }

        offsets[indexPath.row] = tableViewCell.collectionViewOffset
    }
}

extension ViewController: UICollectionViewDataSource {

    func collectionView(collectionView: UICollectionView,
                        numberOfItemsInSection section: Int) -> Int {

        return dataSource[collectionView.tag].count
    }

    func collectionView(collectionView: UICollectionView,
                        cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(CollectionViewCell.identifier,
                                                                         forIndexPath: indexPath) as! CollectionViewCell
        cell.imageView.image = dataSource[collectionView.tag][indexPath.item]
        return cell
    }
}

extension ViewController: UICollectionViewDelegate {

    //タップされたときに、縦が何行目、横が何マス目か判断する
    func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
        print("Line : \(collectionView.tag) / section : \(indexPath.section) / row : \(indexPath.row)")
    }
}

// 画像表示用のダミーデータを取得する機能
extension ViewController {

    class func randomImage() -> UIImage {

        let fileNameIndex = arc4random() % 2
        let fileName = "image\(fileNameIndex + 1).jpg"
        return UIImage.init(named: fileName)!
    }

    class func generateRandomData() -> [[UIImage]] {
        let numberOfRows = 15
        let numberOfItemsPerRow = 10

        return (0..<numberOfRows).map { _ in
            return (0..<numberOfItemsPerRow).map { _ in randomImage() }
        }
    }
}

まとめ

TableViewCellの中にTableViewを配置する方法も試しましたが、
TableViewとTableViewCellの向きをそれぞれ90度回転しなければ、
表示される向きが期待通りに表示できませんでした。

しかし、CollectionViewは、Scrollの向きを縦 or 横に指定できるため
シンプルに実装できました。

参考

以上です。

23
25
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
23
25