コードベースでカスタムTableViewCellを作る

## はじめに
「TableViewControllerを配置しただけだと、何か質素なデザインになってしまう。」「セルとセルの空間をもっと空けたい。」こんな時、あると思います。ここでは`UITableViewCell`を継承したクラスを作成し、好みのカスタムセルを作成する方法を共有します。「こうした方がいい！」と感じた場合はコメントで教えて欲しいです。

![Simulator Screen Shot - iPhone 11 Pro Max - 2021-01-22 at 13.25.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/ada36b23-2d31-42c8-841f-446bb1b0437e.png)
こんな感じのTableViewができます。(上にNavigationBarがありますが。)

## 作り方
### UITableViewCellを継承したクラスを作成する

```Swift
class CustomTableViewCell: UITableViewCell {

}
```
### 初期化の処理をかく
セルの色や文字の場所などのセットアップを行う関数を行うための場所というイメージです。

```Swift
class CustomTableViewCell: UITableViewCell {

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}
```

### 元々のViewを透明にする処理
ここではカスタムビューを表現する方法として、`UITableViewCell`が元々持っているUIView(`contentView`という)とは別のUIViewを用意し、そのUIViewに色をつけるなどしています。なので`contentView`を透明にし、見えないようにします。

```Swift
class CustomTableViewCell: UITableViewCell {

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override func layoutSubviews() {
        contentView.backgroundColor = .clear
        backgroundColor = .clear
    }//追加した
}
```
`contentView`そのものをいじりカスタムセルを作成する方法がありそうですが、技術不足により実装できませんでした。ここら辺のコメント特に募集しています。

### UIViewを用意し、準備する

ここからはコードベースでレイアウトを組むときにやっていることとあまり変わりません。私の過去の記事でもコードベースでレイアウトを組むことについて書いているので、もし詰まったら参考にして下さい。

```Swift
private let backView = UIView()
private func setupUIView() {
        backView.backgroundColor = UIColor(255,189,40)
        backView.layer.cornerRadius = 5
        addSubview(backView)
        backView.snp.makeConstraints {
            $0.width.equalToSuperview().multipliedBy(0.8)
            $0.height.equalTo(contentView.snp.height).multipliedBy(0.9)
            $0.center.equalToSuperview()
        }
}
```
留意点及びやっていることとしては

- レイアウトが楽になるライブラリSnapKitを使っている
- セルの高さが`contentView`の0.9倍になるようにしている
- セルの幅が画面サイズの0.8倍になるようにしている
- 角丸をつけている

です。

### テキストが表示できるようにUILabelを用意する
セルを表示するだけであれば上述したことだけでOKなのですが、表示させる物が一つは必要なので`UILabel`を追加します。

```Swift
let titleLabel = UILabel()
private func setupcellLabel() {
        titleLabel.font = UIFont.systemFont(ofSize: 20)
        backView.addSubview(titleLabel)
        titleLabel.snp.makeConstraints {
            $0.centerX.equalToSuperview().offset(30)
            $0.centerY.equalToSuperview()
            $0.width.equalToSuperview()
        }
}
```

セルから少し右に離れたところにテキストが表示されるようにしています。

### セットアップ関数をイニシャライザにいれる

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

ここまでで一応それっぽく動かすための準備はできました。一旦ここまでのソースコードを載せます。

```Swift
import UIKit

class CustomTableViewCell: UITableViewCell {
    private let backView = UIView()
    let titleLabel = UILabel()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupUIView()
        setupcellLabel()
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupUIView() {
        backView.backgroundColor = UIColor(255,189,40)
        backView.layer.cornerRadius = 5
        addSubview(backView)
        backView.snp.makeConstraints {
            $0.width.equalToSuperview().multipliedBy(0.8)
            $0.height.equalTo(contentView.snp.height).multipliedBy(0.9)
            $0.center.equalToSuperview()
        }
    }
    private func setupcellLabel() {
        titleLabel.font = UIFont.systemFont(ofSize: 20)
        backView.addSubview(titleLabel)
        titleLabel.snp.makeConstraints {
            $0.centerX.equalToSuperview().offset(30)
            $0.centerY.equalToSuperview()
            $0.width.equalToSuperview()
        }
    }
    override func layoutSubviews() {
        contentView.backgroundColor = .clear
        backgroundColor = .clear
    }
}

```
### contentViewのハイライトをなくす
上の通り動かすと、`contentView`がハイライト処理を行ってしまうため、セルを選択したときにしたの画像のようになってしまいます。
![スクリーンショット 2021-01-22 14.09.34.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/d8ad96c8-c299-2220-bd7b-b5c346ab2def.jpeg)

これは良くない🙃
まずはハイライト消します。

```Swift
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupUIView()
        setupcellLabel()
        self.selectionStyle = .none//追加した
}
```
次にセルが選択された際に先ほど自分で定義した`UIView`がハイライト処理されるようにします。

```Swift
override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: true)
        backView.backgroundColor = selected ? UIColor(255,226,146) : UIColor(255,189,40)
    }
```
`backView`の背景色をセルが選択された時は少し明るめの色、そうでない時は元々の色となるようにします。
三項間演算子を使っていますが、全然if文でも書けます。

完成！　あとはTabeViewControllerでこのクラスを使うだけです。

## TableViewでの処理
`UITableViewDataSource`の関数で以下のように書きます。

```Swift
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell//いつもと違う
        return cell
}
```
あとは`register`のところで

```Swift
register(CustomTableViewCell.self, forCellReuseIdentifier: "cell")
```

のように書けば先ほど作ったカスタムセルを使うことができます。