> For the complete documentation index, see [llms.txt](https://ios-course.cornellappdev.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ios-course.cornellappdev.com/resources/textbook/uicollectionview/uicollectionview-setup.md).

# 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="/pages/zt2zUBMvzndhw4vlUuA5" %}
[Broken mention](broken://pages/zt2zUBMvzndhw4vlUuA5)
{% 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="/pages/zt2zUBMvzndhw4vlUuA5" %}
[Broken mention](broken://pages/zt2zUBMvzndhw4vlUuA5)
{% 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 %}
