Swiftのdelegateについて

# はじめに
`TableViewCell`内にボタンを配置した際の、`delegate`についての備忘録になります。


# 動作環境
【Xcode】Version 12.3
【Swift】Version 5.3.2


# 実装後の画面
![スクリーンショット 2021-02-24 23.31.48（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/692635/a14f7eff-1438-e0e5-9f15-5fa6e1ed22ab.png)

```
上のセルから順番にボタンをタップした際の出力結果

0番目のボタンがタップされました！
1番目のボタンがタップされました！
2番目のボタンがタップされました！
3番目のボタンがタップされました！
4番目のボタンがタップされました！
```

# 実装コード
```swift:TableViewCell.swift
import UIKit

// (1)
protocol TableViewCellDelegate {
    func buttonTapAction()
}

class TableViewCell: UITableViewCell {
    
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var button: UIButton!
    
    var delegate: TableViewCellDelegate? // (2)
    
    var cellDone: (()->Void)?
    
    override func awakeFromNib() {
        super.awakeFromNib()
        button.layer.masksToBounds = true
        button.layer.cornerRadius = 5
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }
    
    @IBAction func buttonTapAction(_ sender: Any) {
        cellDone?()
        delegate?.buttonTapAction() // (3)
    }
    
}
```

```swift:ViewController.swift
import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var tableView: UITableView!
    
    private let CELL_IDENTIFIER = "Cell"
    var selectedCellIndex: IndexPath?
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

}

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 5
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: CELL_IDENTIFIER, for: indexPath) as? TableViewCell else {
            return UITableViewCell()
        }
        cell.delegate = self // (4)
        cell.cellDone = { [weak self] in
            self?.selectedCellIndex = indexPath
        }
        cell.label.text = "セル：\(indexPath.row)"
        return cell
    }
    
}

// (4)
extension ViewController: TableViewCellDelegate {
    func buttonTapAction() {
        if let selectedCell = selectedCellIndex {
            print("\(selectedCell.row)番目のボタンがタップされました！")
        }
    }
}
```


# 実装詳細
### 1. protocolで宣言
* `protocol`を宣言します。

```swift
protocol TableViewCellDelegate {
    func buttonTapAction()
}
```

#### `protocol`とは
具体的な処理内容は書かず、__クラスや構造体が実装するプロパティとメソッドを定義する機能__です。
プロトコルを適用したクラスや構造体は、プロトコルに定義されているプロパティとメソッドを__必ず実装__しなければなりません。

### 2. delegateを宣言
* `TableViewCell`クラス内に`delegate`を宣言します。

```swift
var delegate: TableViewCellDelegate?
```

#### `delegate`とは
デザインパターンのひとつです。  
`delegate`は英語で「人に任せる」という意味を持っており、「あるクラスから他のクラスに処理を任せる」というデザインパターンです。
実際の処理はプロトコルを適合する側で実装するため後から変更することができますが、処理を実行する側はどんな処理をするのか意識することなく、呼び出すことができます。


### 3. 受け渡し元の`View`で`delegate`をセット
* 受け渡し元（ `@IBAction func buttonTapAction(_ sender: Any)` ）にセットします。

```swift
delegate?.buttonTapAction()
```


### 4. 受け渡し先の`View`に`delegate`を呼び出す
* 受け渡し先（ `cell` ）に呼び出します。

```swift
cell.delegate = self
```

上記を追加すると、下記エラーが出ます。

```
Cannot assign value of type 'ViewController' to type 'TableViewCellDelegate?'
タイプ「ViewController」の値をタイプ「TableViewCellDelegate？」に割り当てることができません。
```

これは、上記で説明した通り、プロトコルに定義されているプロパティとメソッドを必ず実装しなければならないためになります。
下記を追加し完成です。

```swift
extension ViewController: TableViewCellDelegate {
    func buttonTapAction() {
        // ここに処理内容がはいります。
    }
}
```


# 参考
* [【Swift入門】難解なデリゲート(delegate)の使い方を理解しよう！
](https://www.sejuku.net/blog/33867)
* [【Swift入門】プロトコル(protocol)を使ってみよう！
](https://www.sejuku.net/blog/35550)