01. 목표 : 콜렉션 뷰 가로 스크롤
이번 목표는 아래 이미지와 같이 가로로 넘기는 CollectionView를 만들어보려고 합니다. 아직 CollectionView를 완전히 익힌 상태가 아니라 조금 부족한 부분이 있을겁니다. 이 부분은 계속 공부하면서 개선해 나갈 생각입니다. 그럼 시작해보겠습니다.
02. CollectionViewCell.swift
먼저 셀을 구성해주도록 하겠습니다. SnapKit을 써서 조금 더 쉽게 오토레이아웃을 잡았습니다.
import SnapKit
import Then
class CollectionViewCell: UICollectionViewCell {
lazy var img: UIImageView = UIImageView().then {
$0.image = UIImage(systemName: "photo")
$0.contentMode = .scaleAspectFit
$0.backgroundColor = .yellow
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setupUI()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
fileprivate func setupUI() {
addSubview(img)
backgroundColor = .white
img.snp.makeConstraints {
$0.center.equalToSuperview()
$0.width.height.equalTo(300)
}
}
}
03. ViewController.swift
class ViewController: UIViewController {
lazy var collectionView: UICollectionView = UICollectionView(frame: .zero,
collectionViewLayout: createLayout()).then {
$0.autoresizingMask = [.flexibleWidth, .flexibleHeight]
$0.backgroundColor = .systemBackground
$0.delegate = self
$0.dataSource = self
$0.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
setupUI()
}
}
뷰 컨트롤러에서도 마찬가지로 SnapKit과 Then을 사용했습니다. collectionView를 생성할 때 저는 오토레이아웃을 잡아줄 것이기 때문에 frame을 .zero 옵션을 주었습니다. collectionViewLayout은 이후에 만들 layout을 미리 넣어주었습니다. UICollectionView는 이니셜라이즈 될 때 반드시 layout 파라미터를 갖고 있어야 합니다.
delegate와 dataSource 모두 같은 파일에 정의해 줄 예정이기 때문에 self 로 지정해줍니다. 또 커스텀된 Cell을 사용하기 때문에 커스텀된 셀을 등록해주었습니다.
extension ViewController {
fileprivate func setupUI() {
self.view.addSubview(collectionView)
collectionView.snp.makeConstraints {
$0.center.equalToSuperview()
$0.height.width.equalTo(300)
}
}
}
이 과정 설명은 넘어가겠습니다.
extension ViewController {
func createLayout() -> UICollectionViewLayout {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .groupPagingCentered
return UICollectionViewCompositionalLayout(section: section)
}
}
지금 제가 만들고 싶은 형태의 콜렉션 뷰는 item이 1:1 비율, 그룹도 1:1 비율이기 때문에 위와 같이 설정해주었습니다. 그리고 섹션에 .orthogonalScrollingBehavior 라는 부분이 있는데, 이 부분이 스크롤이 가로로 할 수 있도록 해줍니다. 그리고 옵션인 .groupPagingCentered는 이미지를 넘길 때 group의 중앙이 오도록 하는 방법입니다.
*아래 코드를 참고하세요.
@available(iOS 13.0, *)
public enum UICollectionLayoutSectionOrthogonalScrollingBehavior : Int, @unchecked Sendable {
// default behavior. Section will layout along main layout axis (i.e. configuration.scrollDirection)
case none = 0
// NOTE: For each of the remaining cases, the section content will layout orthogonal to the main layout axis (e.g. main layout axis == .vertical, section will scroll in .horizontal axis)
// Standard scroll view behavior: UIScrollViewDecelerationRateNormal
case continuous = 1
// Scrolling will come to rest on the leading edge of a group boundary
case continuousGroupLeadingBoundary = 2
// Standard scroll view paging behavior (UIScrollViewDecelerationRateFast) with page size == extent of the collection view's bounds
case paging = 3
// Fractional size paging behavior determined by the sections layout group's dimension
case groupPaging = 4
// Same of group paging with additional leading and trailing content insets to center each group's contents along the orthogonal axis
case groupPagingCentered = 5
}
아래는 Delegate와 DataSource를 설정해주었습니다.
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print(#fileID, #function, #line, "- \(indexPath)")
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 10
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell",
for: indexPath) as? CollectionViewCell else {
return UICollectionViewCell()
}
return cell
}
}
예시에서 사용된 것들을 보니까 Delegate와 DataSource를 프로토콜로 사용하지 않고 클로저 형식으로 하던데, 일단 저는 클로저 형식으로 하는 것이 익숙하지 않아 Delegate와 DataSource를 사용했습니다.
또 UICollectionViewDiffableDataSoruce 가 있는데, 이것은 UICollectionViewDataSource와는 다른 방식으로 스스로 계산하고 업데이트하는 똑똑한 프로토콜 같더라구요. 이 부분에 대해서도 추가적인 공부가 필요할 거 같습니다.
그 부분은 다음 글에서 뵙겠습니다.
'Mobile > iOS' 카테고리의 다른 글
[iOS] CollectionView - 댓글창 만들기(1) (0) | 2023.08.06 |
---|---|
[iOS] RxSwift를 이용해서 이전 화면으로 데이터 전달 (0) | 2023.07.20 |
[iOS] CollectionView - CompositionalLayout(1) (0) | 2023.07.06 |
[iOS] CollectionView (0) | 2023.07.06 |
[iOS] UIView 그림자 (0) | 2023.07.01 |