# [UIKit] UICollection 그 시작

# 공식문서를 보자

UICollection 이란 어떤 놈이냐?

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1695221254628/6cf832ed-e251-4217-9759-a9cabe43f750.png align="center")

> An object that manages an ordered collection of data items and presents them using customizable layouts.
> 
> \=&gt; 정렬된 데이터 아이템 모음을 **관리**하고 **보여줌**주는 객체 + 레이아웃 커스텀 가능

대충 이런 뜻이구만 오케이

그럼 어떻게 쓰는건데?

Overview로 같이 고고

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1695222497794/f4793954-bc6f-432c-9e61-3cba21c8f69f.png align="center")

> ... The collection view gets its data from the data source object, stored in the collection view’s [`dataSource`](https://developer.apple.com/documentation/uikit/uicollectionview/1618091-datasource) property. For your data source, you can use a [`UICollectionViewDiffableDataSource`](https://developer.apple.com/documentation/uikit/uicollectionviewdiffabledatasource) object, which provides the behavior you need to simply and efficiently manage updates to your collection view’s data and user interface. Alternatively, you can create a custom data source object by adopting the [`UICollectionViewDataSource`](https://developer.apple.com/documentation/uikit/uicollectionviewdatasource) protocol.
> 
> collection view는 data source object로 부터 데이터 갖고 있음
> 
> collection view의 `dataSource`프로퍼티에 저장함
> 
> 너가 data source 사용하려면 `UICollectionViewDiffableDataSource(클래스)`를 사용해야 함
> 
> 위 클래스는 간단하고 효율적으로 뷰 데이터와 인터페이스 관리해줄거임
> 
> 너가 커스텀으로 데이터를 저장하고 싶다? 그러면 `UICollectionViewDataSource(프로토콜)`채택하면 됨

음 조금은 알 것 같아.

그럼 우선 UICollectionView를 선언하면서 하나씩 배워보자

```swift
import UIKit

class FirstVC: ViewController {
    let collectionView = UICollectionView()
}
```

아주아주 간단한 코드지만 실.행.안.된.다.

Why???

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1695649686630/2ac0f990-af03-4cb6-bc93-72cbca3eb15f.png align="center")

> UIViewCollectionView must be initialized with non-nil layout parameter
> 
> \=&gt; CollectionView는 layout 파라미터가 있는 생성자로 만들어야함.

음? layout이 뭔데?

생성자 공식문서 고고싱

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1695650970866/95f42db2-2434-402f-9450-62cc260cc40c.png align="center")

frame은 말 그대로 frame이고 layout을보자.

> The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil.
> 
> \=&gt; 레이아웃은 아이템을 모으기 위해서 사용됨 CollectionView는 layout을 구체화(메모리 해제 방지)를 위해서 강한 참조로 저장함. 반드시 nil 이 아니여야함

뭔가 뭔가 부족함

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1695653145932/e8cbd39b-fbd5-4725-9c05-2e7863269a26.png align="center")

오오 여기에는 정보가 조금 있구만 보자보자.

> The layout object to use. Use this control to select between the [`UICollectionViewFlowLayout`](https://developer.apple.com/documentation/uikit/uicollectionviewflowlayout) object and a custom layout object that you define.
> 
> 레이아웃을 사용하려면 `UICollectionViewFlowLayout`을 사용하던가 커스텀 레이아웃 사용해라
> 
> When the flow layout is selected, you can also configure the scrolling direction for the collection view’s content and whether the flow layout has header and footer views.
> 
> flow layout이 선택되면 스크롤 방향 선택할 수 있음 그리고 flow layout은 header & footer 뷰를 가질 수 있음
> 
> Enabling header and footer views adds reusable views to your storyboard that you can configure with your header and footer content. You can also create those views programmatically.
> 
> 머리글과 바닥글 보기를 활성화하면 머리글과 바닥글 콘텐츠로 구성할 수 있는 재사용 가능한 보기가 스토리보드에 추가됩니다. 또한 프로그래밍 방식으로 그 뷰를 만들 수 있습니다.
> 
> When a custom layout is selected, you must specify the [`UICollectionViewLayout`](https://developer.apple.com/documentation/uikit/uicollectionviewlayout) subclass to use.
> 
> 만약 커스텀 layout을 사용한다면 너는 UICollectionViewLayout 하위 클래스를 지정해야함

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1695655342237/5e6d68ad-af80-4d41-8684-8207c8792945.png align="center")

> When the Flow layout is selected, the Size inspector for the collection view contains additional attributes for configuring flow layout metrics.
> 
> Flow layout을 선택하면 CollectionView를 위한 Size inspector가 추가 속성을 갖고 있음 flow layout 측정을 위해서
> 
> Use those attributes to configure the size of your cells, the size of headers and footers, the minimum spacing between cells, and any margins around each section of cells.
> 
> 각 속성은
> 
> * 셀 사이즈 / header footer 사이즈 / 셀 간 최소 간격 / 각 셀의 margin 이 있다.
>     

오케이 그럼 UICollectionViewFlowLayout을 보자.

---

그럼 `UICollectionViewDiffableDataSource` 이것 부터 봅시다잉

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1695223965824/1cef75fc-7d31-46d3-8d46-bfc7fba78e8f.png align="center")

```swift
class UICollectionViewDiffableDataSource<SectionIdentifierType, ItemIdentifierType> : NSObject 
where SectionIdentifierType : Hashable, 
      SectionIdentifierType : Sendable, 
      ItemIdentifierType : Hashable, 
      ItemIdentifierType : Sendable
```

아따 선언부도 길다.

> 데이터를 관리할 수 있는 오브젝트 이자 collection view에 cell을 제공하는 오브젝트

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1695297878471/64431544-0fb5-4eab-9309-78dd17712fbc.png align="center")

> diffable data source는 collection view object에 특화되어 있음
> 
> collection view 의 데이터와 UI를 업데이트 관리해줄꺼임 효율적으로.
> 
> 그리고 `UICollectionVIewDataSource`프로토콜도 만족하고 있음.
> 
> 아래와 같이 하면 사용할 수 있음.
> 
> 1. collectionview랑 diff able data source 연결해라
>     
> 2. 셀 공급자 구현하세요. collection view cell을 보여줘야 하잖슴
>     
> 3. 데이터 만드셈
>     
> 4. 데이터 UI에 보여주셈
>     

---

# 예제

확실한 건 예제로 알아보자.

```swift
class ViewController: UIViewController {
    var vm = ViewModel()

    var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        var result = UICollectionView(frame: .zero, collectionViewLayout: layout)
        result.translatesAutoresizingMaskIntoConstraints = false
        
        return result
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        layout()
        
    }

    func layout() {
        view.addSubview(collectionView)
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
        // 1
        collectionView.dataSource = self

        // 2
        collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "MyCell")
    }
}
```

먼저 `UICollectionView`를 ViewController에 부착시켜줬다.

1. `collectionView.dataSource = self`라는 의미는 ViewController가 `UICollectionViewDataSource`프로토콜을 채택하고 Cell과 관련된 정보를 넘겨줄 것이다. 라는 것을 의미한다.
    
2. `collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "MyCell")`의 의미는 collectionView에 CollectionViewCell 이라는 셀을 MyCell 이라는 구분자로 등록할게요~ 라는 의미이다.
    

그럼 어떻게 셀을 표현할까? 먼저 Cell을 만들어야 한다.

```swift
class CollectionViewCell: UICollectionViewCell {
    var imageView: UIImageView = {
        var result = UIImageView()
        result.translatesAutoresizingMaskIntoConstraints = false
        result.contentMode = .scaleAspectFit
        return result
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        addSubview(imageView)
        NSLayoutConstraint.activate([
            imageView.topAnchor.constraint(equalTo: topAnchor),
            imageView.bottomAnchor.constraint(equalTo: bottomAnchor),
            imageView.trailingAnchor.constraint(equalTo: trailingAnchor),
            imageView.leadingAnchor.constraint(equalTo: leadingAnchor),
        ])
        backgroundColor = .blue
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
```

가볍게 `UIImageView`를 갖고 있는 Cell을 만들었다. 그럼 저 Cell을 어떻게 CollectionView에 알려주냐?

```swift
extension ViewController: UICollectionViewDataSource {
    // A
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return vm.images.count
    }
    // B
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let myCell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath) as! CollectionViewCell
        
        if indexPath.item < vm.images.count {
            myCell.imageView.image = vm.images[indexPath.row].image
        } else {
            myCell.backgroundColor = .gray
        }
        return myCell
    }
}
```

바로 ViewController에 `UICollectionViewDataSource`를 채택함으로 알려준다.

A. collectionView에 몇 개의 Cell을 배치할지 알려주는 함수

B. N번째(indexPath)에 어떤 셀을 표시할지 알려주는 함수

그럼 실행 해볼까?

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1698747227133/0e12ef74-dd6a-486c-8da2-cd04f7908db3.png align="center")

아니? 크기가 너무 작잖아!! 왜냐하면 크기를 안 알려줬기 때문!

`UICollectionView`의 Delegate를 설정해줘야 한다. Delegate는 `UICollectionViewDelegateFlowLayout`을 따라야 한다.

```swift
extension ViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: collectionView.contentSize.width / 2 - 10, height: 300)
    }
}
```

자 이제 실행을 해보자. 그럼 아래와 같이 나올 것임

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1698747246075/7d2429d8-a3db-40ca-ae15-1eff87d76fcf.png align="center")

짠! 드디어 CollectionView 처럼 생겼다.

셀의 크기를 편한대로 설정해서 높이 설정 잘 해주면 될 것 같다. 하지만 욕심이 난다.

2열에서 왼쪽은 그림이 위아래로 짧은데, 오른쪽은 길다.

난 높이를 dynamic하게 주고 싶은걸?

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1698747258330/638c896f-3bcc-47db-a6af-204de5fd4c3b.png align="center")

이렇게 말이야. 그럼 어떻게 하지??

이 글에 이걸 담으면 너무 길어질 것 같으니 이건 심화에서 보자.

---

CollectionView 너무 어렵! 하지만 피할 수 없는 걸!
