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
- map
- uiscrollview
- Refactoring
- Observable
- 리펙터링
- uitableview
- 애니메이션
- ribs
- UICollectionView
- Clean Code
- swift documentation
- swiftUI
- 스위프트
- HIG
- combine
- ios
- 리팩토링
- 리펙토링
- SWIFT
- MVVM
- 클린 코드
- Protocol
- rxswift
- clean architecture
- collectionview
- tableView
- Xcode
- UITextView
- Human interface guide
- RxCocoa
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 2. UICollectionViewCompositionalLayout - 둘러보기 (SupplementaryView, Header, Footer) 본문
iOS 응용 (swift)
[iOS - swift] 2. UICollectionViewCompositionalLayout - 둘러보기 (SupplementaryView, Header, Footer)
jake-kim 2022. 4. 20. 23:291. UICollectionViewCompositionalLayout - 개념 (section, group, item)
2. UICollectionViewCompositionalLayout - 둘러보기1 (SupplementaryView, Header, Footer)
3. UICollectionViewCompositionalLayout - 개념 (DecorationView, Badge)
5. UICollectionViewCompositionalLayout - 응용 (유튜브 뮤직 앱 UI 구현)
SupplementaryView와 DecorationView
- 애플에서의 개념으로 이해하면, UICollectionView는 크게 3가지로 존재
- item cell: 메인 데이터가 있는 UI
- supplementaryView: 메인 데이터를 보충해주는 UI
- decorationView: 데이터를 표출하지 않는 단순 UI (컬렉션 뷰의 모델과 독립적인 뷰) - 배경, 섹션 하이라이트에 사용
- SupplementaryView
- header, footer도 supplementaryView 중 하나이고 아래처럼 section을 기준으로 대표적으로 위와 아래에 위치할 수 있는 뷰
- compositionalLayout을 사용하면 한 섹션을 기준으로 9가지의 방향으로 표현이 가능
- DecodationView
- 데이터를 표출하지 않고 collectionView전체에 관한 단순 UI
Header, Footer 사용 방법
- Header나 Footer에 들어갈 뷰 준비
- UICollectionReusableView를 상속받은 커스텀 뷰
import UIKit
final class MyHeaderFooterView: UICollectionReusableView {
private lazy var label: UILabel = {
let label = UILabel()
label.textColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(label)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .gray
NSLayoutConstraint.activate([
self.label.centerYAnchor.constraint(equalTo: self.centerYAnchor),
self.label.centerXAnchor.constraint(equalTo: self.centerXAnchor),
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func prepareForReuse() {
super.prepareForReuse()
self.prepare(text: nil)
}
func prepare(text: String?) {
self.label.text = text
}
}
- register 부분
- collectionView.register(:forSupplementaryViewOfKind:withReuseIdentifier:) 사용
- 델리게이트에서 dequeueReusableSupplementaryView(ofKind:withReuseIdentifier:for:)
// 등록 - collectionView
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.register(MyHeaderFooterView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "MyHeaderView")
view.register(MyHeaderFooterView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "MyFooterView")
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
// dataSource 델리게이트
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionView.elementKindSectionHeader:
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "MyHeaderFooterView", for: indexPath) as! MyHeaderFooterView
return header
case UICollectionView.elementKindSectionFooter:
let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "MyHeaderFooterView", for: indexPath) as! MyHeaderFooterView
return footer
default:
return UICollectionReusableView()
}
}
- compostionalLayout안에서 header, footer에 관한 레이아웃을 정의
- NSCollectionLayoutBoundarySupplementaryItem 인스턴스를 생성
- section.boundarySupplementaryItems에 인스턴스 주입
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)
// header / footer
let headerFooterSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(100.0))
let header = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: headerFooterSize,
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top
)
let footer = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: headerFooterSize,
elementKind: UICollectionView.elementKindSectionFooter,
alignment: .bottom
)
section.boundarySupplementaryItems = [header, footer]
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
}
}
}
좌측에 SupplementaryView 넣는 방법
- 좌측에 view를 넣기 위해서 Header와 Footer처럼 register 준비
// collectionView init 부분
view.register(MyHeaderFooterView.self, forSupplementaryViewOfKind: "MyLeftView", withReuseIdentifier: "MyLeftView")
// dataSource 델리게이트
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionView.elementKindSectionHeader:
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "MyHeaderView", for: indexPath) as! MyHeaderFooterView
header.prepare(text: "헤더 타이틀")
return header
case UICollectionView.elementKindSectionFooter:
let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "MyFooterView", for: indexPath) as! MyHeaderFooterView
footer.prepare(text: "푸터 타이틀")
return footer
case "MyLeftView":
let leftView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "MyLeftView", for: indexPath) as! MyHeaderFooterView
leftView.prepare(text: "left 타이틀")
return leftView
default:
return UICollectionReusableView()
}
}
- header, footer와 동일하게 NSCollectionLayoutBoundarySupplementaryView 인스턴스에서 .leading 옵션 사용
static func getLayout() -> UICollectionViewCompositionalLayout {
UICollectionViewCompositionalLayout { (section, env) -> NSCollectionLayoutSection? in
...
let leftSize = NSCollectionLayoutSize(widthDimension: .absolute(100), heightDimension: .absolute(700))
let left = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: leftSize,
elementKind: "MyLeftView",
alignment: .leading
)
section.boundarySupplementaryItems = [header, footer, left]
return section
...
- 아래처럼 왼쪽에 supplementary view 생성 완료
spacing 주는 방법
- 아래는 group의 left supplementaryView의 fractionalWidth를 0.1로 주고, group의 fractinoalWidth를 0.9로 준 상태
- 여백이 왼쪽에 생기고, 그 여백에 leftView가 들어가는 방법?
- group의 edgeSpacing 값을 부여
- .flexible()값은 뷰의 크기에 따라 변하고, .fix()는 절대값
- 왼쪽에 flexible()값을 부여하여, 왼쪽이 줄어들거나 늘어나도록 설정
// in getLayout()
// Group
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.9),
heightDimension: .fractionalHeight(groupFractionalHeightFraction)
)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
group.edgeSpacing = NSCollectionLayoutEdgeSpacing(
leading: .flexible(0),
top: nil,
trailing: nil,
bottom: nil
)
* 전체 코드: https://github.com/JK0369/ExCompositoinalLayout2
* 참고
https://ebookreading.net/view/book/EB9781484212424_13.html
https://www.raywenderlich.com/5436806-modern-collection-views-with-compositional-layouts
'iOS 응용 (swift)' 카테고리의 다른 글
Comments