> 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/uitableview/uitableview-setup.md).

# UITableView Setup

### Create a Custom UITableViewCell

Creating a `UITableViewCell` is very similar to how we have been creating views inside of a `UIViewController`. We still define properties for the view and data, initialize those views, add them as a subview, and constrain those views. However, when working inside of a `UITableViewCell` there are some slight modifications that we will need to make. Follow these steps:

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

   ```swift
   override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
       super.init(style: style, reuseIdentifier: reuseIdentifier)
   }

   // `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 that 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 `UITableViewCell`
class CustomTableViewCell: UITableViewCell {
    // 3. Create view properties
    private let label = UILabel()

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

    // 2. Create the following init
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        // 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

In Step 6 above, notice that we have a **reuse identifier**. First, let’s imagine we have a table view that contains a list of all students at Cornell. How many cells would we have? Thousands! Remember, each cell is a separate view and if we have thousands of views, that’s a lot of memory being used! The workaround for this would be to create only the views needed on the screen at one time. If a cell were to go off of the screen, Swift will ***dequeue*** this cell for another cell to be created. This is the reason why the cells in a `UITableView` look very similar! It makes it very efficient to dequeue a cell and reuse it.

How does Swift know which cell to “pick up” and reuse? A **reuse identifier** is used to associate a cell that is being dequeued with another cell that is about to be rendered.

### Setting Up a UITableView

A `UITableView` 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 constraints

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

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

```swift
tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: CustomTableViewCell.reuse)
```

6. **Set the `UITableView` delegate** (create an extension just like any other protocol)
   * See [UITableViewDelegate](#6-uitableviewdelegate) section below
7. **Set the `UITableView` dataSource** (create an extension just like any other protocol)
   * See [UITableViewDataSource](#7-uitableviewdatasource) section below

### 6: UITableViewDelegate

The purpose of a `UITableViewDelegate` is to add functionality to the table view. A class conforming to the protocol `UITableViewDelegate` **does not** have any required functions to implement; however, the two most common functions to implement are: `heightForRowAt` and `didSelectRowAt`.

```swift
// If our cell has a fixed height, then implement this function.
// If our cell has a dynamic height, do not implement this function and rely on AutoLayout.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return <height> // the height of the cell
}
```

```swift
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    // Perform some operation when cell is tapped
}
```

{% hint style="info" %}
**Tip: If we start typing `heightForRowAt` or `didSelectRowAt`, Xcode will autofill the function header for us!**
{% endhint %}

### 7: UITableViewDataSource

In contrast to `UITableViewDelegate`, there are ***two*** **required** *functions* to implement: `cellForRowAt` and `numberOfRowsInSection`.

For `numberOfRowsInSection`, we want to provide the number of rows (cells) for a section. Usually, this is the size of our data model array. For example, if our table view listed out all students at Cornell, we would have a data model representing an array of Student objects. The number of rows would be the size of the array (use `.count` to get the size).

```swift
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return dataModelArray.count
}
```

The purpose of `cellForRowAt` is to determine the cell class to use (in addition to registering the cell) as well as configuring the cell (by calling the `configure` function). The following code is for a custom cell class called `CustomTableViewCell`:

```swift
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: CustomTableViewCell.reuse, for: indexPath) as? CustomTableViewCell else { return UITableViewCell() }
    
    let dataModel = dataModelArray[indexPath.row]
    cell.configure(...) // pass in our dataModel to the configure function in our custom cell class
    return cell
}
```

Let’s go over this function line by line:

1. First, we ***dequeue*** the cell with the given *reuse identifier*. This gives us a `UITableViewCell`. Now, we need to *cast* it to our custom type by using the `as?` keyword. This returns an optional type of our custom cell. We unwrap it by using a `guard let` (or we could use an `if let`). If the casting failed and the optional holds `nil`, we return a basic `UITableViewCell`.
2. Then, we need to identify which data this cell will hold. Most of the time, we will have some data model array (such as an array of students). To determine the position a cell is located inside of a table view, we use `indexPath.row` which returns an `Int`. We could then use this value to access an element inside of our data model array.
3. Next, we would need to configure our cell with the data model that we retrieve in Step 2. We can pass this information into our custom cell class’s `configure` function that we implemented earlier to configure the cell’s views such as changing a UILabel’s text.
4. Finally, we return the configured custom cell.

### Complete Code

```swift
class ViewController: UIViewController {
    // 1. Create the view
    private let tableView = UITableView()

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

	setupTableView() // 2. Configure the view
    }
		
    // 2. Configure the view
    private func setupTableView() {
        // 5. Register Custom Cell
        tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: CustomTableViewCell.reuse)
        tableView.delegate = self // 6
	tableView.dataSource = self // 7
		
	view.addSubview(tableView) // 3
	tableView.translatesAutoresizingMaskIntoConstraints = false // 4
		
	// 4. Set constraints
    }
}

// 6. Conform to `UITableViewDelegate`
extension ViewController: UITableViewDelegate {
    // `heightForRowAt` (optional)
    // `didSelectRowAt` (optional)
    // Additional functions here
}

// 7. Conform to `UITableViewDataSource`
extension ViewController: UITableViewDataSource {
    // `cellForRowAt`
    // `numberOfRowsInSection`
    // Additional functions here
}
```
