Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- 애니메이션
- uitableview
- Refactoring
- MVVM
- Xcode
- ios
- RxCocoa
- swift documentation
- clean architecture
- tableView
- 리팩토링
- UICollectionView
- uiscrollview
- SWIFT
- HIG
- 스위프트
- 리펙토링
- combine
- swiftUI
- Clean Code
- ribs
- UITextView
- rxswift
- 클린 코드
- collectionview
- map
- Human interface guide
- 리펙터링
- Protocol
- Observable
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] UITableVIewCell 안에 CollectionView 넣기 (Cell안의 UICollectionView) 본문
UI 컴포넌트 (swift)
[iOS - swift] UITableVIewCell 안에 CollectionView 넣기 (Cell안의 UICollectionView)
jake-kim 2022. 6. 1. 23:13
Cell안에 UICollectionView를 넣는 구현 아이디어
- UITableViewCell 안에 UICollectionView가 있어야하므로, UITableViewCell을 서브클래싱한 커스텀 셀에 UICollectionView를 정의
- UITableViewCell의 커스텀셀에서 dataSources를 처리하도록 구현
- UITableViewCell에서 items들을 들고 있도록 구현
구현 - 모델링
- 셀에 표시할 타입 정의
- thumbnail 케이스는 위 사진에서 MyTableViewCellOne에서 사용할 타입
- collection 케이스는 위 사진에서 MyTableViewCellTwo에서 사용할 타입 (이 셀에는 collectionView가 존재하므로, collectionView에서 사용할 데이터도 포함)
- 모델링의 핵심은 collectionView의 데이터도 같이 넘기는 것
import Foundation
import UIKit
enum MyItem: Equatable {
case thumbnail(UIImage?, String) // thumbnailImage, name
case collection(String, [CollectionViewItem]) // name, collection
}
enum CollectionViewItem: Equatable {
case color(UIColor)
}
- 예제로 사용할 데이터)
// ViewController.swift
var items: [MyItem] = [
.thumbnail(UIImage(named: "flower"), "iOS flower"),
.thumbnail(UIImage(named: "flower"), "iOS thumbnail"),
.thumbnail(UIImage(named: "flower"), "iOS image"),
.collection("<Color List>", [
.color(randomColor),
.color(randomColor),
.color(randomColor),
.color(randomColor),
.color(randomColor),
.color(randomColor),
.color(randomColor),
.color(randomColor),
]),
.thumbnail(UIImage(named: "flower"), "iOS capture"),
.thumbnail(UIImage(named: "flower"), "iOS development"),
.thumbnail(UIImage(named: "flower"), "iOS jake"),
]
구현 - MyCollectionViewCell
(가장 뎁스가 깊은, MyCollectionViewCell 부터 구현)
- UICollectionViewCell을 서브클래싱하고 UIView가 하나 존재
- 해당 셀의 크기는 해당 셀을 사용하는 UICollectionViewFlowLayout에서 정의해줄것이므로 UIView의 레이아웃에서 width, height 정의는 x
- 이에 관한 자세한 개념은 UICollectionView로 수평 스크롤 뷰 구현 방법 참고
import UIKit
final class MyCollectionViewCell: UICollectionViewCell {
static let id = "MyCollectionViewCell"
private let colorView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect) {
super.init(frame: frame)
self.contentView.addSubview(self.colorView)
NSLayoutConstraint.activate([
self.colorView.leftAnchor.constraint(equalTo: self.contentView.leftAnchor),
self.colorView.rightAnchor.constraint(equalTo: self.contentView.rightAnchor),
self.colorView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor),
self.colorView.topAnchor.constraint(equalTo: self.contentView.topAnchor),
])
}
override func prepareForReuse() {
super.prepareForReuse()
self.prepare(color: nil)
}
func prepare(color: UIColor?) {
self.colorView.backgroundColor = color
}
}
구현 - MyTableViewCellTwo
- 핵심이 되는 셀이며, 해당 셀에서 UICollectionView를 가지고 있고 UICollectionViewDataSource까지 처리하는 셀
- cellHeight까지 정의
- 해당 셀을 사용하는 UITableViewDelegate의 heightForRowAt에서 높이 값을 주기 위함 (수평 스크롤 뷰를 위해 셀의 높이 고정)
- 해당 셀에서 UICollectionViewFlowLayout 인스턴스의 itemSize에서 height값에 해당 값을 부여
- cellHeight까지 정의
import UIKit
final class MyTableViewCellTwo: UITableViewCell {
static let id = "MyTableViewCellTwo"
static let cellHeight = 300.0
}
(cellHeight값을 사용하는 ViewController)
// ViewController.swift
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch self.items[indexPath.item] {
case .thumbnail:
return UITableView.automaticDimension
case .collection:
return MyTableViewCellTwo.cellHeight
}
}
- MyTableViewCellTwo에 사용할 UI 및 레이아웃 정의
- 핵심 부분은 Cell에서 collectionView에 데이터를 뿌려줄 items를 가지고 있고, collectionView.dataSource = self로 선언한 코드
// MyTableViewCellTwo.swift
private let label: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 25)
label.numberOfLines = 0
label.textColor = .gray
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let collectionViewFlowLayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 8.0
layout.minimumInteritemSpacing = 0
layout.itemSize = .init(width: 300, height: cellHeight)
return layout
}()
lazy var collectionView: UICollectionView = {
let view = UICollectionView(frame: .zero, collectionViewLayout: self.collectionViewFlowLayout)
view.isScrollEnabled = true
view.showsHorizontalScrollIndicator = false
view.showsVerticalScrollIndicator = true
view.contentInset = .zero
view.backgroundColor = .clear
view.clipsToBounds = true
view.register(MyCollectionViewCell.self, forCellWithReuseIdentifier: MyCollectionViewCell.id)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private var items = [CollectionViewItem]() // <-
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.collectionView.dataSource = self // <-
self.contentView.addSubview(self.label)
self.contentView.addSubview(self.collectionView)
NSLayoutConstraint.activate([
self.label.leftAnchor.constraint(equalTo: self.contentView.leftAnchor),
self.label.rightAnchor.constraint(equalTo: self.contentView.rightAnchor),
self.label.topAnchor.constraint(equalTo: self.contentView.topAnchor),
])
NSLayoutConstraint.activate([
self.collectionView.leftAnchor.constraint(equalTo: self.contentView.leftAnchor),
self.collectionView.rightAnchor.constraint(equalTo: self.contentView.rightAnchor),
self.collectionView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor),
self.collectionView.topAnchor.constraint(equalTo: self.label.bottomAnchor),
])
}
- prepare에서 collectionView의 items도 받도록 구현
// MyTableViewCellTwo.swift
override func prepareForReuse() {
super.prepareForReuse()
self.prepare(name: nil, items: [])
}
func prepare(name: String?, items: [CollectionViewItem]) {
self.label.text = name
self.items = items
}
- dataSource 처리
// MyTableViewCellTwo.swift
extension MyTableViewCellTwo: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
self.items.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch self.items[indexPath.item] {
case let .color(color):
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCollectionViewCell.id, for: indexPath) as! MyCollectionViewCell
cell.prepare(color: color)
return cell
}
}
}
구현 - MyTableViewCellOne
(특징이 없고 일반적인 UITableViewCell이므로 생략)
구현 - 사용하는 ViewController 쪽
- tableView 정의
// ViewController.swift
import UIKit
class ViewController: UIViewController {
private let tableView: UITableView = {
let view = UITableView()
view.allowsSelection = false
view.backgroundColor = .clear
view.separatorStyle = .none
view.bounces = true
view.showsVerticalScrollIndicator = true
view.contentInset = .zero
view.register(MyTableViewCellOne.self, forCellReuseIdentifier: MyTableViewCellOne.id)
view.register(MyTableViewCellTwo.self, forCellReuseIdentifier: MyTableViewCellTwo.id)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
...
}
- 테스트로 사용할 데이터 선언
var items: [MyItem] = [
.thumbnail(UIImage(named: "flower"), "iOS flower"),
.thumbnail(UIImage(named: "flower"), "iOS thumbnail"),
.thumbnail(UIImage(named: "flower"), "iOS image"),
.collection("<Color List>", [
.color(randomColor),
.color(randomColor),
.color(randomColor),
.color(randomColor),
.color(randomColor),
.color(randomColor),
.color(randomColor),
.color(randomColor),
]),
.thumbnail(UIImage(named: "flower"), "iOS capture"),
.thumbnail(UIImage(named: "flower"), "iOS development"),
.thumbnail(UIImage(named: "flower"), "iOS jake"),
]
- tableView 데이터 소스 처리
- 일반적인 테이블 뷰 데이터 소스처리하는 방식이랑 동일
- 핵심은 collectionView가 있는 tableViewCell에는 collectionView에 사용할 item 배열을 넘겨주는 것
- cell.prepare(name: name, items: items)
- collectionView가 있는 tableViewCell은 수평 스크롤로 구현하려고 했으므로, heightForRowAt메소드에서 위에서 정했던 cellHeight값을 반환
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch self.items[indexPath.item] {
case let .thumbnail(image, name):
let cell = tableView.dequeueReusableCell(withIdentifier: MyTableViewCellOne.id, for: indexPath) as! MyTableViewCellOne
cell.prepare(image: image, name: name)
return cell
case let .collection(name, items):
let cell = tableView.dequeueReusableCell(withIdentifier: MyTableViewCellTwo.id, for: indexPath) as! MyTableViewCellTwo
cell.prepare(name: name, items: items)
return cell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch self.items[indexPath.item] {
case .thumbnail:
return UITableView.automaticDimension
case .collection:
return MyTableViewCellTwo.cellHeight
}
}
}
'UI 컴포넌트 (swift)' 카테고리의 다른 글
Comments