17
9

More than 3 years have passed since last update.

【Swift】TableViewとCollectionViewでよく見るUIを再現してみる

Last updated at Posted at 2019-12-20

はじめに

CA Tech Dojo/Challenge/JOB Advent Calendar 2019の20日目は@misakiagataが書かせていただきます。
次の日、21日目は@yawn_yawn_yawn_さんです!
私は、2019年8月にCATechDojo(Kotlin編)に参加させていただき、非常に多くの学びを得ることができました!
今回はCAのアドベントカレンダーということで、AbemaTVのUIにもあるような、コンテンツをカテゴリごとに横スクロールできるやつを作ってみようかと思います。(あ、iOSです)

完成形

こんな感じでTableViewの中にCollectionViewをのせて、縦スクロール×横スクロールできるよく見るUIです。
ガッキーGIF.gif

ざっくり概要説明

全体の階層は以下のようになっています。

  • ViewController (ViewController.swift)
  • TableView(ContentsTableView.swift)
  • TableViewCell(ContentsViewCell.swift)
  • CollectionView(ContentsCollectionView.swift)
  • CollectionViewCell(ContentsCollectionViewCell.swift)

(画像はこちらから拝借致しました)。
スクリーンショット 2019-12-20 17.18.55.png

今回はTableViewCell、CollectionViewCellともにxibファイルを使っての実装となっており、それぞれのファイルの階層は以下のようになっています(左がTableViewCell.xibで右がCollectionViewCell.xibのイメージ)。
スクリーンショット 2019-12-20 17.32.54.png

CollectionViewCell

CollectionView.xibにはUIImageViewをのせているだけです。便宜上UIImageViewにはAssets.xcasesに登録した画像をセットしています。
スクリーンショット 2019-12-20 23.13.31.png

ContentsCollectionViewCell.swift
import UIKit

class ContentsCollectionViewCell: UICollectionViewCell {

    @IBOutlet var contentsImageView: UIImageView!

    override func awakeFromNib() {
        super.awakeFromNib()

        configureUI()
    }

    func configureUI() {
        contentsImageView.layer.cornerRadius = 4
        contentsImageView.layer.masksToBounds = true
        contentsImageView.image = UIImage(named: "yui")
    }
}

TableViewCell

TableViewCell.xib中にUICollectionViewを配置しています。
スクリーンショット 2019-12-20 23.12.50.png

スクロールした時にバーが出現しないように以下のチェックは外してあります。また、ScrollDirectionHorizontalにしておきましょう。
スクリーンショット 2019-12-20 23.14.56.png

ContentsTableViewCell.swift
import UIKit

class ContentsTableViewCell: UITableViewCell {

    @IBOutlet var contentsCollectionView: UICollectionView!
    @IBOutlet var titleLabel: UILabel!

    var collectionViewOffset: CGFloat {
        get {
            return contentsCollectionView.contentOffset.x
        }

        set {
            contentsCollectionView.contentOffset.x = newValue
        }
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code

        let nib = UINib(nibName: "ContentsCollectionViewCell", bundle: .main)
        contentsCollectionView.register(nib, forCellWithReuseIdentifier: "ContentsCollectionViewCell")
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

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

        contentsCollectionView.delegate = dataSourceDelegate
        contentsCollectionView.dataSource = dataSourceDelegate
        contentsCollectionView.reloadData()
    }
}

ViewController

ViewControllerでは各種デリゲートメソッドなどなどを実装しています。(便宜上一部数値をベタ書きしてます。)

ContentsViewController.swift
import UIKit

class ContentsViewController: UIViewController {

    @IBOutlet var contentsTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        configureUI()
    }

    func configureUI() {
        contentsTableView.delegate = self
        contentsTableView.dataSource = self
        let nib = UINib(nibName: "ContentsTableViewCell", bundle: .main)
        contentsTableView.register(nib, forCellReuseIdentifier: "ContentsTableViewCell")
        contentsTableView.tableFooterView = UIView()
    }

}

extension ContentsViewController: UITableViewDelegate, UITableViewDataSource {

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 160
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        guard let cell = cell as? ContentsTableViewCell else { return }
        cell.setCollectionViewDataSourceDelegate(dataSourceDelegate: self, forRow: indexPath.section)
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ContentsTableViewCell", for: indexPath) as! ContentsTableViewCell
        cell.setCollectionViewDataSourceDelegate(dataSourceDelegate: self, forRow: indexPath.row)
        cell.contentsCollectionView.reloadData()
        return cell
    }
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
    }
}

extension ContentsViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 10
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ContentsCollectionViewCell", for: indexPath) as! ContentsCollectionViewCell
        return cell

    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: 104, height: 136)
    }
}

Storyboard

Main.storyboardはこんな感じです。
スクリーンショット 2019-12-20 23.12.06.png

まとめ

今回はかなりシンプルなサンプル作成だったので簡略化して書いている部分も多いですが、TableViewCellの行によってセットするCollectionViewCellを変えたりして色々とカスタマイズできそうです。

参考文献

https://ashfurrow.com/blog/putting-a-uicollectionview-in-a-uitableviewcell-in-swift/
https://qiita.com/akspect/items/f996dd09cb05051e09ca

17
9
1

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
17
9