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 | 31 |
Tags
- swiftUI
- combine
- uitableview
- 스위프트
- map
- uiscrollview
- Observable
- HIG
- 리팩토링
- clean architecture
- UICollectionView
- swift documentation
- SWIFT
- rxswift
- Xcode
- Refactoring
- 애니메이션
- tableView
- ios
- ribs
- MVVM
- 클린 코드
- Protocol
- Clean Code
- collectionview
- RxCocoa
- 리펙토링
- UITextView
- Human interface guide
- 리펙터링
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 1. UICollectionViewCompositionalLayout - 개념 (section, group, item) 본문
iOS 응용 (swift)
[iOS - swift] 1. UICollectionViewCompositionalLayout - 개념 (section, group, item)
jake-kim 2022. 4. 19. 22:451. UICollectionViewCompositionalLayout - 개념 (section, group, item)
2. UICollectionViewCompositionalLayout - 개념 SupplementaryView, Header, Footer)
3. UICollectionViewCompositionalLayout - 개념 (DecorationView, Badge)
5. UICollectionViewCompositionalLayout - 응용 (유튜브 뮤직 앱 UI 구현)
CompositionalLayout 기본 개념
- CompositionalLayout의 구성은 Section + Group + Item
- 여기서 핵심은 group이며, group은 한 화면에 들어가는 item들을 묶는 단위
- 레이아웃을 구성할 때 section, group, item의 레이아웃을 설정하여 구현
- CompositionalLayout은 하나의 CollectionView에 섹션별로 다른 layout을 구성하기가 쉬운 장점이 존재
NSCollectionLayoutDimension
CollectionView의 item 사이즈를 정하는 방법은 3가지
- .absolute - 고정 크기
- .estimated - 런타임에 변경
- .fractional - 비율
let absoluteSize = NSCollectionLayoutSize(
widthDimension: .absolute(32),
heightDimension: .absolute(32)
)
let estimatedSize = NSCollectionLayoutSize(
widthDimension: .estimated(120),
heightDimension: .estimated(120)
)
let fractionalSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.2),
heightDimension: .fractionalHeight(0.2)
)
Section, Group, Item 사용 방법
- collectionView, dataSource 정의
final class ViewController: UIViewController {
private lazy var collectionView: UICollectionView = {
let view = UICollectionView(frame: .zero, collectionViewLayout: self.compositionalLayout) // TODO: compositionalLayout
view.isScrollEnabled = true
view.showsHorizontalScrollIndicator = false
view.showsVerticalScrollIndicator = true
view.scrollIndicatorInsets = UIEdgeInsets(top: -2, left: 0, bottom: 0, right: 4)
view.contentInset = .zero
view.backgroundColor = .clear
view.clipsToBounds = true
view.register(MyCell.self, forCellWithReuseIdentifier: "MyCell") // TODO: MyCell
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private let dataSource: [MySection] = [
.first((1...30).map(String.init).map(MySection.FirstItem.init(value:))),
.second((31...60).map(String.init).map(MySection.SecondItem.init(value:))),
]
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(self.collectionView)
NSLayoutConstraint.activate([
self.collectionView.leftAnchor.constraint(equalTo: self.view.leftAnchor),
self.collectionView.rightAnchor.constraint(equalTo: self.view.rightAnchor),
self.collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
self.collectionView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
])
self.collectionView.dataSource = self
}
}
enum MySection {
case first([FirstItem])
case second([SecondItem])
struct FirstItem {
let value: String
}
struct SecondItem {
let value: String
}
}
- MyCell과 dataSource 구현
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
self.dataSource.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch self.dataSource[section] {
case let .first(items):
return items.count
case let .second(items):
return items.count
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath) as! MyCell
switch self.dataSource[indexPath.section] {
case let .first(items):
cell.prepare(text: items[indexPath.item].value)
case let .second(items):
cell.prepare(text: items[indexPath.item].value)
}
return cell
}
}
final class MyCell: UICollectionViewCell {
let label: UILabel = {
let label = UILabel()
label.textColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
required init?(coder: NSCoder) {
fatalError()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.contentView.backgroundColor = UIColor(
red: CGFloat(drand48()),
green: CGFloat(drand48()),
blue: CGFloat(drand48()),
alpha: 1.0
)
self.contentView.addSubview(self.label)
NSLayoutConstraint.activate([
self.label.centerYAnchor.constraint(equalTo: self.contentView.centerYAnchor),
self.label.centerXAnchor.constraint(equalTo: self.contentView.centerXAnchor),
])
}
override func prepareForReuse() {
super.prepareForReuse()
self.prepare(text: "")
}
func prepare(text: String) {
self.label.text = text
}
}
compositionalLayout 정의
- Item 정의 -> Group 정의 -> Section 정의
private let compositionalLayout: UICollectionViewCompositionalLayout = {
let itemFractionalWidthFraction = 1.0 / 3.0 // horizontal 3개의 셀
let groupFractionalHeightFraction = 1.0 / 4.0 // vertical 4개의 셀
let itemInset: CGFloat = 2.5
// Item
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(itemFractionalWidthFraction),
heightDimension: .fractionalHeight(1)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: itemInset, leading: itemInset, bottom: itemInset, trailing: itemInset)
// Group
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .fractionalHeight(groupFractionalHeightFraction)
)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
// Section
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: itemInset, leading: itemInset, bottom: itemInset, trailing: itemInset)
return UICollectionViewCompositionalLayout(section: section)
}()
- itemSize계산할 필요 없이, 화면에 셀을 3개만 띄우고 싶은 경우, group의 count 속성을 사용하면 편리
private func getGridSection() -> NSCollectionLayoutSection {
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.3),
heightDimension: .fractionalHeight(1.0)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8)
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(0.3)
)
// collectionView의 width에 3개의 아이템이 위치하도록 하는 것
let group = NSCollectionLayoutGroup.horizontal(
layoutSize: groupSize,
subitem: item,
count: 3
)
let section = NSCollectionLayoutSection(group: group)
return section
}
Section 별로 다른 레이아웃 사용 방법
- CompositionalLayout은 하나의 CollectionView에 섹션별로 다른 layout을 구성하기가 쉬운 장점이 존재
- UICollectionViewCompositionalLayout 인스턴스에서 section별로 분기문을 써서 layout을 다르게하기가 간편
- 아래처럼 collectionView를 초기화 할 때 getLayout() 메소드를 따로 정의하여 이 메소드만 주입
private lazy var collectionView: UICollectionView = {
let view = UICollectionView(frame: .zero, collectionViewLayout: Self.getLayout()) // <-
view.isScrollEnabled = true
view.showsHorizontalScrollIndicator = false
view.showsVerticalScrollIndicator = true
view.scrollIndicatorInsets = UIEdgeInsets(top: -2, left: 0, bottom: 0, right: 4)
view.contentInset = .zero
view.backgroundColor = .clear
view.clipsToBounds = true
view.register(MyCell.self, forCellWithReuseIdentifier: "MyCell")
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
- getLayout() 정의
- UICollectionViewCompositionlalLayout의 클로저에서 section 인수를 통해 분기문 작성
static func getLayout() -> UICollectionViewCompositionalLayout {
UICollectionViewCompositionalLayout { (section, env) -> NSCollectionLayoutSection? in
switch section {
case 0:
let itemFractionalWidthFraction = 1.0 / 3.0 // horizontal 3개의 셀
let groupFractionalHeightFraction = 1.0 / 4.0 // vertical 4개의 셀
let itemInset: CGFloat = 2.5
// Item
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(itemFractionalWidthFraction),
heightDimension: .fractionalHeight(1)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: itemInset, leading: itemInset, bottom: itemInset, trailing: itemInset)
// Group
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .fractionalHeight(groupFractionalHeightFraction)
)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
// Section
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: itemInset, leading: itemInset, bottom: itemInset, trailing: itemInset)
return section
default:
let itemFractionalWidthFraction = 1.0 / 5.0 // horizontal 5개의 셀
let groupFractionalHeightFraction = 1.0 / 4.0 // vertical 4개의 셀
let itemInset: CGFloat = 2.5
// Item
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(itemFractionalWidthFraction),
heightDimension: .fractionalHeight(1)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: itemInset, leading: itemInset, bottom: itemInset, trailing: itemInset)
// Group
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .fractionalHeight(groupFractionalHeightFraction)
)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
// Section
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: itemInset, leading: itemInset, bottom: itemInset, trailing: itemInset)
return section
}
}
}
- 좀 더 간결하게 구현하려면, 아래와 같이 MySection(rawValue: sectionNumber)를 넣어서 사용
- 구체적인 코드는 이곳 참고
private func getLayout() -> UICollectionViewLayout {
return UICollectionViewCompositionalLayout { sectionIndex, env -> NSCollectionLayoutSection? in
switch self.dataSource[sectionIndex] {
case .main:
return self.listSection()
case .sub:
return self.gridSection()
}
}
}
* 전체 코드: https://github.com/JK0369/ExComposionalLayout
* 참고
https://demian-develop.tistory.com/22
https://www.raywenderlich.com/5436806-modern-collection-views-with-compositional-layouts
'iOS 응용 (swift)' 카테고리의 다른 글
Comments