이전 블로그에서는 가장 기본적인 CompositionalLayout
을 설정하고 호출하여 사용하는 방법을 정리했는데 이번에는 제가 만들려고 하는 화면에 맞는 사이즈를 item, group, section
에 입력하여 layout을 설정하는 것에 대해서 작성하고자 합니다.
고민한 부분
UICollectionViewCell의 적용과 콘텐츠 크기의 동적 크기 변경
CompositionalLayout
을 사용하여 화면에 배치 및 설정하기 위해서는
UICollectionViewCell에 들어가는 아이템과 하나 이상의 아이템이 포함되는 group, 전체 화면에 배치되게 될 Section의 layout을 세팅 해야 합니다.
그래서 저의 경우 제일 큰 부분인 section부터 배치를 잡고, group, item 순으로 layout을 만들고자 했습니다.
그런데 실제로는 반대가 item먼저 설정하고, group, section 순으로 확인하는게 좋을거 같아요.
아이템의 경우 UICollectionViewCell을 가져오는것을 통해 즉각적으로 layout이 확인 되지만 section은 아래 구성 요소들이 새팅 되어야 확인하기 용이해서 그렇습니다.
물론 Debug View Hierarchy를 사용하면 쉽게 확인이 되지만 앱이 많이 열러있어서 그런지 제 컴퓨터가 힘들어 하더군요…
그리고 각각의 CollectionViewCell에 들어가는 이미지, 텍스트의 layout 크기가 item, group의 layout 설정에 따라 크게 변하게 되어 많은 어려움을 겪게 되었습니다. item의 layout을 정확히 맞추더라도 group혹은 CollectionViewCell에 설정된 autolayout에 따라서 설정하고자 했던 크기가 계속해서 변하는 이슈가 나타났습니다.
제가 이 부분에 대해서는 완벽하지는 않지만 CollectionViewCell, 아이템, 그룹에 대한 높이, 넓이에 대한 layout을 모두 비율로 설정하여 어느 정도는 해결할 수 있게 되었습니다.
구현
그래서 먼저 UICollectionViewCell과 item에 대한 layout설정하는 것을 확인해 보려고 합니다
아래 Section을 만들고자 하고 구독 icon과 title을 하나의 Cell item으로 설정하게 되었습니다


Cell의 image, label의 아이템을 스텍뷰를 사용했습니다.
// 이미지 아이콘
private let channelImage: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.image = UIImage(named: "homeChannelSample1")
return imageView
}()
// 텍스트 라벨
private let channelLabel: UILabel = {
let label = AuthenticationLabel()
label.setTextWithStyle(
text: "channelName",
size: 12,
weight: .regualr
)
label.textColor = .gray606060
return label
}()
// 스택 뷰
private lazy var subTitleItemStackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [channelImage, channelLabel])
stackView.axis = .vertical
stackView.spacing = 10
stackView.alignment = .center
stackView.distribution = .fill
return stackView
}()
....
func addView() {
addSubview(subTitleItemStackView)
}
func setLayout() {
subTitleItemStackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
channelImage.topAnchor.constraint(equalTo: topAnchor, constant: 14),
channelImage.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 0.8),
channelImage.heightAnchor.constraint(equalTo: channelImage.widthAnchor),
])
NSLayoutConstraint.activate([
subTitleItemStackView.leadingAnchor.constraint(equalTo: leadingAnchor),
subTitleItemStackView.topAnchor.constraint(equalTo: topAnchor),
subTitleItemStackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 10),
subTitleItemStackView.trailingAnchor.constraint(equalTo: trailingAnchor),
])
}
아이템 1개의 결과 이미지

위 CollectionViewCell 생성이 완료되었다면
뷰컨에서 CompostionalLayout의 아이템, 그룹, 섹션의 layout을 설정 합니다.
private func getChannelSection() -> NSCollectionLayoutSection {
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.19),
heightDimension: .fractionalHeight(0.17)
)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
....
return section
}
아이템에 해당되는 CollectionViewCell의 layout에 맞게 item을 설정하고자 width, height 모두 1.0으로 설정했습니다. (다만 둘 이상의 아이템이 하나의 그룹에 들어가야 하는 경우 값을 변경해줘야할 필요 있음)
하나의 그룹안에 하나 이상의 아이템이 들어가고 해당 그룹은 할당된 섹션의 width, height 기준으로 비율로서 계산되어집니다.
width 비율 확인

저는 group의 width(channel group width / ViewController width), height(channel group heigth / ViewController height)를 계산해서 입력하였고 다음은 section에 대한 설정을 해줍니다
private func getChannelSection() -> NSCollectionLayoutSection {
// item , group 설정
....
let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .continuous
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
return section
}
section.orthogonalScrollingBehavior은 기존에 CollectionView를 사용했을 때 가로 스크롤을 사용하려면 하나의 Cell안에 CollectionView를 더 넣어서 진행하는 방식대신 orthogonalScrollingBehavior를 사용하면 쉽게 해당 섹션에 대한 가로 스크롤이 가능합니다.
처음 수평스크롤을 진행하고 싶었을때는 NSCollectionLayoutGroup.horizontal를 사용하면 되는줄 알았지만
이와는 별개로 orthogonalScrollingBehavior를 추가해야만 수평 스크롤이 가능하다는 것을 확인 했습니다.
섹션 설정이 완료되면 CollectionView를 생성하고 compostional layout을 설정하는 함수를 호출하여 넣어줍니다.
private func getLayout() -> UICollectionViewLayout {
return UICollectionViewCompositionalLayout { sectionIndex, env -> NSCollectionLayoutSection? in
switch self.homeSource[sectionIndex] {
case .channel:
// channel에 대한 Compostional layout
return self.getChannelSection()
case .keyword:
// keyword에 대한 Compostional layout
// return self.getKeywordSection()
case .video:
// video에 대한 Compostional layout
// return self.getVideoSection()
}
}
}
private lazy var homeCollectionView: UICollectionView = {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: self.getLayout())
collectionView.translatesAutoresizingMaskIntoConstraints = false
return collectionView
}()
마지막으로 homeCollectionView에 대한 addSubView, layout 배치를 진행하면 아래와 같이 수평 스크롤이 가능하게 됩니다.

제가 구현한 작업을 요약해서 설명해자면
- CollectionViewCell 구현
- ViewController에서 CompostionalLayout의 item, group, section 설정
- CollectionView 인스턴스 생성 및 사용
저는 추가로 2개의 섹션 keyword, video에 해당하는 항목들을 추가 구현해야 되기 때문에 keyword, Video section의 CollectionViewCell 구현, CompostionalLayout 설정을 진행하였습니다.

'iOS' 카테고리의 다른 글
[iOS] 콜렉션뷰 Youtube API 활용 #2 (2) | 2024.06.06 |
---|---|
[iOS] 콜렉션뷰 Youtube API 활용 #1 (2) | 2024.06.03 |
[iOS] CompositionalLayout을 한번 사용해보자 #2 (0) | 2024.05.18 |
[iOS] CompositionalLayout을 한번 사용해보자 #1 (0) | 2024.05.13 |
[iOS] Concurrency(async & await) (0) | 2024.03.31 |