【Swift】TableViewのCellの中に配置したButtonのTagにindex番号か、データのIDをセットする

#何故書くか
業務の中で、Cellの中にボタンを配置し、そのボタンにIndex番号、またはidをtagとしてもたせ、APIを叩くということを実現したかったのですが、なかなかうまくいかず詰まったので、備忘録がてら + あまり調べても当てはまる手法がなかったのでどなたかの助けになればと思い書かせていただきます。

またかなり無理やりな手法のためアンチパターンの可能性も否めないのでその際はご指摘いただけますと幸いです。

#ButtonのTagにIdをセットする
idをボタンに持たせる場合にはCellの中に見えないラベルを用意し、そこにidを反映させました。

```swift
//下記にtableViewに反映させたいデータが格納されている
var productDataList:[productDataStruct] = []

//省略

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        
    //透明のラベルにidを付随させる
    let idLabel = cell.contentView.viewWithTag(-1) as! UILabel
    idLabel.text = String(productDataList[indexPath.row].id)

    if cell.contentView.viewWithTag(-2) == nil {
        for obj in productDataList {
            if cell.contentView.viewWithTag(obj.id) != nil {
                let toRequestBtn = productCell.contentView.viewWithTag(obj.id) as! UIButton
                toRequestBtn.tag = Int(idLabel.text!)!
            }
        }
    }else{
        let toRequestBtn = productCell.contentView.viewWithTag(-2) as! UIButton
        toRequestBtn.tag = Int(idLabel.text!)!
    }
    return cell
}
```

まず、ユニークなidとtagの値が被らないように表示しないidのLabelには「-1」、そしてButtonのtagには「-2」を初期の値としてセットしています。

そして、if文で、-2のtagがcellの中に存在するかによってButtonのtagをセットするための処理を分岐しています。

もし存在していればそのままラベルにセットされている値をtagにセットし、存在していなければ、データの中からtagにセットされているidを探し、それを元にButtonを取得し、そのcellにセットされているidをセットします。

何故こんなややこしいことをすること言うと、TableViewは非表示になったcellを再利用するために、
・IndexPathがあてにならない
・動的にtagをセットできない（-2のタグは最初の描画で消滅する）
・-2のtagがなくなってしまってはButtonが取得できなくなる
といったことが起きるためです。

再利用が発生すると、Buttonに振られているtagは、再利用される前のcellのidが付随しているために、まずは付随しているidを探すと言う作業が必要になってくるわけです。

#ButtonのtagにIndexPathの数値を動的にセットする。

続いてButtonのTagにIndexの値を動的にセットする方法についてです。
Indexの値を動的にセットしなければならない時の一例といて、それぞれのセルの状態を取得する必要があるときなどが上げられるかと思います。
例えば下記のコード。
UISwitchが配置されており、この値を使用しAPIを叩きたいときなどに有効かと思います。

```swift
//下記にtableViewに反映させたいデータが格納されている
var productDataList:[productDataStruct] = []

//省略

//連番の配列を用意
let indexList = [Int](1...productDataList.count)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

    let saleFlag = cell.contentView.viewWithTag(-1) as! UISwitch
    saleFlag.isOn = productDataList[indexPath.row].send_flag

    if cell.contentView.viewWithTag(-2) == nil {
        for index in indexList {
            if cell.contentView.viewWithTag(index) != nil {
                let toRequestBtn = productCell.contentView.viewWithTag(index) as! UIButton
                toRequestBtn.tag = indexPath.row + 1
            }
        }
    }else{
        let toRequestBtn = productCell.contentView.viewWithTag(-2) as! UIButton
        toRequestBtn.tag = Int(idLabel.text!)!
    }
    return cell
}
```

注意点として、Tagに０番を指定するとnilになるので、indexの値をセットする問いには必ず１を足す必要があります。
そのため、ButtonをTapされindex番号を投げ処理をする際には必ず、１を引くことを忘れないようにしなければなりません。

これも大枠は先ほどと同じで違いとしては、
・インデックス番号に１を足したもののリストを用意している
・ButtonのTagは１を引かないと、ずれたデータが取れてしまう
といったところです。

-2のTagがcell上に存在するかしないかで、一致するインデックス番号を探すかを分岐するといったメインの部分は大方同じです。

#最後に
なんとか手探りで進めているiOSの開発なので、もし問題点がありましたら、ご指摘いただけますとありがたいです🙏
ここまで読んでいただきありがとうございました！