🎃
TableViewCellにCollectionViewを配置するメモ
TableViewCell内にCollectionViewを配置する際に、処理の移譲先で少し悩んだので備忘録として残しておきます。
基本的には、TableViewCell内のCollectionViewを配置した場合でも、ViewControllerに直接CollectionViewを配置した場合と考え方は変わらず、ViewControllerにdelegateとdataSorceの責務を移譲します。
また、CollectionViewCell内にUIButtonを設置する場合は、CollectionViewCellDelegateのようなprotocolを宣言して、ViewController側でそのprotocolに準拠してあげれば、CollectionViewCell内のボタンタップイベントをViewControllerで実装できます。
以下、簡単なデモ用のコード。今回はcollectionViewCell内にimageViewとUIButtonを設置して、UIButtonの処理をViewControllerに移譲するようにしています。
Card.swift
import Foundation
struct Card: Codable {
var id: Int?
var title: String?
var imageUrl: String?
}
CustomTableViewCell.swift
import UIKit
class CustomTableViewCell: UITableViewCell {
@IBOutlet weak var collectionView: UICollectionView!
override func awakeFromNib() {
super.awakeFromNib()
let customCollectionViewCellName = "CustomCollectionViewCell"
collectionView.register(UINib(nibName: customCollectionViewCellName, bundle: nil), forCellWithReuseIdentifier: customCollectionViewCellName)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
CustomCollectionViewCell.swift
import UIKit
protocol CustomCollectionViewCellDelegate: AnyObject {
func showDetail(cell: CustomCollectionViewCell)
}
class CustomCollectionViewCell: UICollectionViewCell {
weak var delegate: CustomCollectionViewCellDelegate?
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var imageview: UIImageView!
@IBAction func tappedButton(_ sender: Any) {
delegate?.showDetail(cell: self)
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
func configure(card: Card) {
titleLabel.text = card.title
let image = getImageByUrl(url: card.imageUrl!)
imageview.image = image
}
func getImageByUrl(url: String) -> UIImage{
let url = URL(string: url)
do {
let data = try Data(contentsOf: url!)
return UIImage(data: data)!
} catch let err {
print("Error : \(err.localizedDescription)")
}
return UIImage()
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
var cards: [Card] = []
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
initTable()
// 今回はdemoのためcards配列に適当な値を入れてしまう。
cards.append(Card(id: 1, title: "test", imageUrl: "https://www.pakutaso.com/shared/img/thumb/susipaku211-app90962_TP_V.jpg"))
tableView.reloadData()
}
func presentToDetail(card: Card) {
// カード詳細画面への遷移処理。
// 例えば、Card.idをパラメータとしてwebView開くなど。
print("presentToDetail")
}
}
// MARK: UITableViewDelegate, UITableViewDataSource
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func initTable() {
tableView.delegate = self
tableView.dataSource = self
let customTableViewCellName = "CustomTableViewCell"
tableView.register(UINib(nibName: customTableViewCellName, bundle: nil), forCellReuseIdentifier: customTableViewCellName)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell", for: indexPath as IndexPath) as! CustomTableViewCell
cell.collectionView.delegate = self
cell.collectionView.dataSource = self
return cell
}
// cellの高さ。今回はdemoのため適当な数値。
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 200
}
}
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
cards.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCollectionViewCell", for: indexPath as IndexPath) as! CustomCollectionViewCell
cell.configure(card: cards[indexPath.row])
cell.delegate = self // CustomCollectionViewCellDelegate
return cell
}
}
extension ViewController: CustomCollectionViewCellDelegate {
func showDetail(cell: CustomCollectionViewCell) {
// collectionViewCoellのindexPathを特定するために、まずはtableViewCellを特定する。
// 今回の場合は、tableViewCellは一つしかない想定なので、決め打ちでIndexPath(row: 0, section: 0)を指定。
guard let tableViewCell = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? CustomTableViewCell, let indexPath = tableViewCell.collectionView.indexPath(for: cell) else { return }
presentToDetail(card: cards[indexPath.row])
}
}
以上。
Discussion