# UICollectionView Setup

### Create a Custom UICollectionViewCell

Creating a `UICollectionViewCell` is very similar to creating a `UITableViewCell`. However, there are just **minor** syntax changes. Follow these steps:

{% hint style="info" %}
**Only Steps 1 and 2 are different when creating a `UICollectionViewCell` compared to a `UITableViewCell`**
{% endhint %}

1. Our custom class needs to be a subclass of `UICollectionViewCell`
   * `class CustomCollectionViewCell: UICollectionViewCell { }`
2. Create the following initializer:

   ```swift
   // Compare this with a `UITableViewCell`
   override init(frame: CGRect) {
       super.init(frame: frame)
   }

   // `required init` here
   ```
3. Determine what views to create and write a helper function to initialize its properties.
   * For example, if we need to display some text, we would create a `UILabel` and create a helper function to initialize its font, font color, etc. Note that we do not know anything about the data yet, so the property `.text` of the `UILabel` will not be initialized **yet**.
4. Inside of the helper function, add the views we created as a subview to `contentView` and constrain the view with respect to `contentView`. Then call the helper function inside of the initializer.
   * **This is one of the main differences from what we have been doing before. Instead of referencing `view`, we will be using `contentView`. Note: we do not need to use `safeAreaLayoutGuide` here.**
5. Create a `configure` function (do not make `private`) that will take in some data as a parameter, and configure our views.
   * For example, we could write a function that takes in a `String` and sets `UILabel.text` property equal to the value passed in.
6. Create a reuse identifier for this cell: `static let reuse = "<reuse_identifier>"`
   * See “Dequeuing Cells” below for more information.

```swift
// 1. Subclass of `UICollectionViewCell`
class CustomCollectionViewCell: UICollectionViewCell {
    // 3. Create view properties
    private let label = UILabel()

    // 6. Create a reuse identifier
    static let reuse = "CustomCollectionViewCellReuse"

    // 2. Create the following init
    override init(frame: CGRect) {
        super.init(frame: frame)

        // 4. Call helper functions
	setupLabel()
    }
		
    // 2. `required init` here

    // 5. `configure` function (do not make private)
    func configure(newText: String) {
	label.text = newText
	// Configure additional views here
    }

    // 3. Set Up View Helpers
    private func setupLabel() {
	// 3. Initialize the label's properties

	// 4. Add as subview to `contentView`

	// 4. Constrain with respect to `contentView`
    }
}
```

### Dequeuing Cells

The idea behind this concept is the exact same for that of a `UITableViewCell`. Read it here:

{% content-ref url="broken-reference" %}
[Broken link](https://ios-course.cornellappdev.com/resources/textbook/uicollectionview/broken-reference)
{% endcontent-ref %}

### Setting Up a UICollectionView

A `UICollectionView` is just like any other `UIView` that we've worked with thus far. We've initialized the view by doing the following steps:

1. Create the view
2. Configure the view by changing its properties
3. Adding the view as a subview to some parent view
4. Enable auto layout and set up constraint

With a `UICollectionView`, we do the exact same thing but with additional steps:

5. **Register a `UICollectionViewCell`**
   * For example, if we had a custom class called `CustomCollectionViewCell` with a static reuse constant called `reuse`, we would use the following code:

```swift
collectionView.register(CustomCollectionViewCell.self, forCellWithReuseIdentifier: CustomCollectionViewCell.reuse)
```

6. **Set the `UICollectionView` delegate** (create an extension just like any other protocol)
   * See [UICollectionViewDelegate](#6-uicollectionviewdelegate) section below
7. **Set the `UICollectionView` dataSource** (create an extension just like any other protocol)
   * See [UICollectionViewDataSource](#7-uicollectionviewdatasource) section below

Every step that we mentioned above is very similar to that of a `UITableViewCell`; however, there is 1 more additional step.

8. **Initialize the collection view with a `UICollectionViewFlowLayout` and conform to `UICollectionViewDelegateFlowLayout`**
   * See [UICollectionViewFlowLayout](#8-uicollectionviewflowlayout) section below

### 6: UICollectionViewDelegate

The purpose of a `UICollectionViewDelegate` is to add functionality to the collection view. A class conforming to the protocol `UICollectionViewDelegate` **does not** have any required functions to implement; however, the most common function to implement is: `didSelectItemAt`.

```swift
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    // Perform some operation when cell is tapped
}
```

### 7: UICollectionViewDataSource

In contrast to `UICollectionViewDelegate`, there are ***two*** **required** *functions* to implement: `cellForItemAt` and `numberOfItemsInSection`. The idea is exactly the same as that of a table view, but with minor syntax changes (”item” instead of “row”).

```swift
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return dataModelArray.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    guard let cell = collectionView.dequeueReusableCell(withIdentifier: CustomCollectionViewCell.reuse, for: indexPath) as? CustomCollectionViewCell else { return UICollectionViewCell() }
    
    let dataModel = dataModelArray[indexPath.row]
    cell.configure(...) // pass in our dataModel to the configure function in our custom cell class
    return cell
}
```

Read the details here:

{% content-ref url="broken-reference" %}
[Broken link](https://ios-course.cornellappdev.com/resources/textbook/uicollectionview/broken-reference)
{% endcontent-ref %}

### 8: UICollectionViewFlowLayout

Everything we’ve mentioned earlier is very similar to a table view but with minor syntax changes. However, there is one more additional step that is required by a collection view that gives it customization benefits.

#### Create a FlowLayout and Initialize CollectionView

Inside of the helper function that sets up the collection view, add the following lines of code:

```swift
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = // .vertical or .horizontal
layout.minimumLineSpacing = // (optional) spacing amount
layout.minimumInteritemSpacing = // (optional) spacing amount

// Initialize CollectionView with the layout
collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
```

Let’s go over this line by line:

1. Create a `UICollectionViewFlowLayout` instance
2. (REQUIRED) Set the collection view’s scroll direction: `.vertical` or `.horizontal`
3. (OPTIONAL) Set the spacing between each *line* (top and bottom)
4. (OPTIONAL) Set the spacing between each *item* (left and right)
5. Initialize CollectionView with the layout we just created

#### Conform to UICollectionViewDelegateFlowLayout

In the previous step, we configured the collection view’s **layout**. Remember how the `UICollectionViewDelegate` did not have a `heightForRowAt` function like a table view does? Well that’s because each item (cell) has a customizable height ***and width*** whereas in a table view, we could only customize the row’s height. To do this, just create an extension and conform to `UICollectionViewDelegateFlowLayout` and add this function:

```swift
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: <width>, height: <height>)
}
```

{% hint style="info" %}
**If we want to configure the size of each item relative to the size of the collection view, we can use `collectionView.frame`. For example, if we want the item cell to be half of the width of the collection view, we can do `collectionView.frame.width / 2`.**
{% endhint %}

### Complete Code

```swift
class ViewController: UIViewController {
    // 1. Create the property BUT DONT INITIALIZE IT YET
    // Note that this is different from a table view
    private var collectionView: UICollectionView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Additional setup here

	setupCollectionView() // 2. Configure the view
    }
		
    // 2. Configure the view
    private func setupCollectionView() {
	// 8. Create a FlowLayout
	let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = // .vertical or .horizontal
        layout.minimumLineSpacing = // (optional) spacing amount
        layout.minimumInteritemSpacing = // (optional) spacing amount

	// Initialize CollectionView with the layout
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)				
	collectionView.register(CustomTableViewCell.self, forCellWithReuseIdentifier: CustomTableViewCell.reuse) // 5
	collectionView.delegate = self // 6
	collectionView.dataSource = self // 7
		
	view.addSubview(collectionView) // 3
	collectionView.translatesAutoresizingMaskIntoConstraints = false // 4
		
	// 4. Set constraints
    }
}

// 6. Conform to `UICollectionViewDelegate`
extension ViewController: UICollectionViewDelegate {
    // `didSelectItemAt` (optional)
    // Additional functions here
}

// 7. Conform to `UICollectionViewDataSource`
extension ViewController: UICollectionViewDataSource {
    // `cellForItemAt`
    // `numberOfItemInSection`
    // Additional functions here
}

// 8. Conform to `UICollectionViewDelegateFlowLayout`
extension ViewController: UICollectionViewDelegateFlowLayout {
    // `sizeForItemAt`
    // Additional functions here
}
```

### Comparing with a UITableView

A lot of the material mentioned above is very repetitive and already seen in a `UITableView`. For comparison purposes, here are the main differences between the two when setting them up:

1. Create a subclass of `UICollectionViewCell` instead
   * The `init` function is different
2. A flow layout is required when initializing the collection view
   * You may have noticed that instead of using `private let collectionView = UICollectionView()` we used `private var collectionView: UICollectionView!`. The reason for this is because we have to pass in a **layout** when initializing the collection view. By replacing it with this line, we are making a promise that we will initialize the collection view later. (There is a cleaner way to do this but it is a bit advanced for now).
3. You must conform to `UICollectionViewDelegateFlowLayout`
   * Implement the `sizeForItemAt` function
4. Syntax: use “items” instead of “rows”
   * Ex: `cellForItemAt` instead of `cellForRowAt`. However, the implementation is the exact same.

{% hint style="info" %}
**Tip: If we start typing the function name such as `cellForItemAt`, we can choose which one we want and Xcode will autofill the function header for us!**
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ios-course.cornellappdev.com/resources/textbook/uicollectionview/uicollectionview-setup.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
