# Binding TableView

## เริ่มจาก DataSource

อย่างแรกเราเริ่มจากบอกว่า ViewController ของเราเป็น **UITableViewDataSource** ก่อน เพื่อให้ TableView ที่เราลากใส่ใน Main.storyboard สามารถดึงข้อมูลจาก controller นี้มาแสดง ซึ่งหลังจากพิมพ์ UITableViewDataSource เสร็จ Xcode จะ Error

![Xcode แสดง error ว่าเรายังไม่ conform protocol UITableViewDataSource](https://759097476-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FuqWEUpYllBlpWTYMEIgR%2Fuploads%2FjSqZKHtjcilXUSMcBM1m%2FScreen%20Shot%202022-06-26%20at%202.53.39%20PM.png?alt=media\&token=64e1ba5f-14f9-4d5e-aab6-1d7805020276)

ให้เรากดปุ่ม Fix แล้ว Xcode จะสร้างฟังก์ชั่น 2 อันเพิ่มมาให้เรา

อันแรก **tableView(\_:numberOfRowsInSection:)** เป็นช่องทางให้ tableView ถามว่าจะมีกี่รายการ

อันที่สอง **tableView(\_:cellForRowAt:)** ใช้ให้ TableView ขอว่าแต่ละรายการหน้าตาเป็นอย่างไร

![Xcode จะสร้างฟังก์ชั่น 2 อันเพิ่มมาให้เรา](https://759097476-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FuqWEUpYllBlpWTYMEIgR%2Fuploads%2FT6pDPi4g3beLun0VrX5N%2FScreen%20Shot%202022-06-26%20at%202.53.59%20PM.png?alt=media\&token=0617ac56-0606-4801-8e60-b0c39141de1b)

โค้ดของเราจะเป็นแบบนี้ ให้เราตอบไปก่อนว่ามี 0 รายการ แต่ละอันให้เป็น cell เปล่า ๆ ไปก่อน

{% code title="ViewController.swift" %}

```swift
import UIKit

class ViewController: UIViewController, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        0
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        UITableViewCell()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }
}
```

{% endcode %}

## ใส่ Todo เข้ามา

จากนั้นให้เราสร้าง Todo ขึ้นมาแล้วให้ **tableView(\_:numberOfRowsInSection:)** ตอบเป็นจำนวน items ใน todo ด้วย **todo.totalItems** ที่เราเตรียมไว้

{% code title="ViewController.swift" %}

```swift
import UIKit

class ViewController: UIViewController, UITableViewDataSource {

    var todo = Todo()

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        todo.totalItems
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        UITableViewCell()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        todo.add(item: TodoItem(title: "Buy milk", isDone: false))
        todo.add(item: TodoItem(title: "Learning Swift", isDone: false))
    }
}
```

{% endcode %}

**บรรทัดที่ 5** สร้าง todo ขึ้นมาเก็บไว้ตั้งแต่ viewController นี้ถูกสร้างขึ้นมา\
**บรรทัดที่ 8** ตอบเป็นจำนวน items ใน todo ด้วย **todo.totalItems** ที่เราเตรียมไว้\
**บรรทัดที่ 17 - 18** ลองเพิ่ม todoItem เข้าไปก่อน

## Refactor หน่อย ขี้เกียจพิมพ์

จะเห็นว่าเวลาเราสร้าง TodoItem เราจะต้องมาใส่ isDone เป็น false ตลอด เราน่าจะให้มันใส่ false เป็น default เลยได้ไหมถ้าไม่ระบุ

ให้เราแก้โค้ด เพิ่ม **= false** ใน init

{% code title="TodoItem.swift" %}

```swift
import Foundation

class TodoItem {
    var title: String
    var isDone: Bool

    init(title: String, isDone: Bool = false) {
        self.title = title
        self.isDone = isDone
    }
}
```

{% endcode %}

แค่นี้เราก็จะเรียกได้แบบนี้

```swift
TodoItem(title: "Buy milk")
TodoItem(title: "Learning Swift", isDone: true)
```

แล้วเราจะได้ ViewController ของเราเป็นแบบนี้

{% code title="ViewController.swift" %}

```swift
import UIKit

class ViewController: UIViewController, UITableViewDataSource {

    var todo = Todo()

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        todo.totalItems
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        UITableViewCell()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        todo.add(item: TodoItem(title: "Buy milk"))
        todo.add(item: TodoItem(title: "Learning Swift"))
    }
}
```

{% endcode %}

เมื่อเรารันจะได้แบบนี้

![](https://759097476-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FuqWEUpYllBlpWTYMEIgR%2Fuploads%2FL0q7T4NeGEBfhzwFWlp9%2FScreen%20Shot%202022-06-26%20at%203.02.56%20PM.png?alt=media\&token=9ccc3d89-cfb1-472c-9d45-55bfb50cd244)

**อ้าว ไม่มีอะไรเกิดขึ้น อ๋อ สงสัยยังไม่ได้สร้าง cell ใน storyboard**

## มาสร้าง TableViewCell กัน

กลับมาที่ Main.storyboard กันเหมือนเดิม&#x20;

1. คลิ๊กเลือก TableView ของเรา
2. ไปที่ Attributes Inspector เพิ่ม **Prototype cells** เป็น 1
3. TableView จะมี TableViewCell โผล่ขึ้นมา
4. ให้เลือกที่ TableViewCell แล้ว เปิด Attributes Inspector เลือก **Style เป็น Basic** ใส่ Identifier เป็น **todoItemCell**

![สร้าง Prototype cell แบบ Basic](https://759097476-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FuqWEUpYllBlpWTYMEIgR%2Fuploads%2FU759UKg1w4coZnjyVO83%2FJun-26-2022%203-06-47%20PM.gif?alt=media\&token=977448e1-5f79-49a1-836e-a88f2fdbd492)

## ผูก DataSource ด้วย

เราต้องบอก TableView ใน storyboard ด้วยว่า ViewController เป็น dataSource นะ เพราะว่า ViewController แค่บอกว่าฉันเป็น dataSource ได้นะ แต่ไม่มีอะไรบอกว่า เป็นของใคร TableView ไหน

**วิธีผูก dataSource แบบแรกใน document outline**&#x20;

1. กด **ctrl ค้างไว้**แล้ว**คลิ๊กลาก**จาก tableView ไปหา ViewController&#x20;
2. เลือก dataSource

![ผูกใน Document Outline](https://759097476-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FuqWEUpYllBlpWTYMEIgR%2Fuploads%2FyVsChd2kcpRAUiU9KP2H%2FJun-26-2022%203-10-44%20PM.gif?alt=media\&token=8c53cf1c-d5ed-4070-81ed-d9ff033607c3)

**วิธีผูกผ่าน Connections Inspector**&#x20;

1. ให้เลือก TableView เสร็จแล้วเปิด Connections Inspector
2. ลาก dataSource มาที่ ไอคอนสีเหลืองของ ViewController&#x20;

![ผูกผ่าน Connections Inspector](https://759097476-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FuqWEUpYllBlpWTYMEIgR%2Fuploads%2FF9GUFcK0mXa4yKOBGi37%2FJun-26-2022%203-14-23%20PM.gif?alt=media\&token=d8843351-6440-4188-a57f-2b042532934d)

ถ้ารันดูก็ยังว่างเปล่าไม่มีอะไรใน TableView

## คืน TableViewCell ที่เราสร้าง

ใน **tableView(\_:cellForRowAt:)** ยังคืนเป็น Cell เปล่า ๆ อยู่ ให้เราแก้เป็น&#x20;

```swift
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    tableView.dequeueReusableCell(withIdentifier: "todoItemCell", for: indexPath)
}
```

{% hint style="danger" %}
Identifier ที่เราตั้งให้ TableViewCell ใน storyboard จะต้องตรงกันกับโค้ดตอนที่เรา dequeueReusableCell ไม่เช่นนั้น แอปจะ Crash เวลารัน
{% endhint %}

โค้ดสุดท้ายจะเป็นแบบนี้

{% code title="ViewController.swift" %}

```swift
import UIKit

class ViewController: UIViewController, UITableViewDataSource {

    var todo = Todo()

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        todo.totalItems
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        tableView.dequeueReusableCell(withIdentifier: "todoItemCell", for: indexPath)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        todo.add(item: TodoItem(title: "Buy milk"))
        todo.add(item: TodoItem(title: "Learning Swift"))
    }
}
```

{% endcode %}

พอรันแล้วเราจะได้ Title มาสองอัน ทำไมหล่ะ ไม่เป็น Buy milk กับ Learning Swift

![](https://759097476-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FuqWEUpYllBlpWTYMEIgR%2Fuploads%2FyX8aQUy36vtlL9S5xgAg%2FScreen%20Shot%202022-06-26%20at%203.17.18%20PM.png?alt=media\&token=d17dba12-8301-4d71-9873-16f373fd5eee)

เพราะเรายังไม่ได้ผูก Title ใน todoItems กับ Cell เลย
