[Swift]UITableViewCellを使ってTableViewに色々表示する

# これは何？

僕がSwiftで分報アプリを作るにあたってタイムラインの実装をした時にTableViewにいろんな情報を表示させる時にちょっと苦戦したのでその戦いの記録です。

# 完成イメージ

完成のイメージはこれです↓↓↓
![timeline.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264906/bbd926bf-36d6-e5e6-aee7-bf44c29aa348.png)



# UITableViewCellクラスを利用してセルに色々表示する

セルに色々表示するに当たって、以下のことを順番にやっていきます。

- 表示したいものを取得する
- UITableViewCellクラスを作成する
- TableViewにUITableViewCellクラスを紐づける
- プロパティに表示したい内容をぶち込む

## 表示したいものを取得する

今回私は、分報アプリということで**数十字程度の文字列**と**日付**をDomainの実装の段階で用意しました。
Domainでの実装についてはこちらをご覧ください↓↓↓
[Swift+FirestoreでSNSライクなアプリを作る(Domain篇)](https://qiita.com/27ma4_ryusei/items/b1a36ea5c5733cbb985b)

これらの情報はReportという構造体に入れています。

```swift:Report.swift
struct Report {
    var content: String
    var createdAt: Timestamp
}
```

通常、Firestoreから日付をそのまま取得すると**Timestamp型**で返ってきます。
今回カスタムセルには基本的に文字列しか入れられないので、まずはTimestampをString型に変換します。
やり方としては、Formatterを用意し、一度Date型として定義をし直してStringにキャストしています。


```swift:TimelineViewController.swift
extension TimelineViewController: UITableViewDataSource
{
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let formatter = DateFormatter()
        formatter.dateFormat = DateFormatter.dateFormat(fromTemplate: "ydMMM", options: 0, locale: Locale(identifier: "ja_JP"))

        let date = self.postedReports[indexPath.row].createdAt.dateValue()
        let dateText = formatter.string(from: date as Date)
```

## UITableViewCellクラスを作成する

セル１つ１つの内容を管理するクラスを作成します。
このクラスを利用することで、表示する内容やセルのスタイルを設定することが出来ます。
詳しくは[Apple公式ドキュメント](https://developer.apple.com/documentation/uikit/uitableviewcell)をご覧ください。

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

基本的にはドキュメントの記載通り、`init()`メソッドをオーバーライドするだけで良いのですが、
一つ注目してほしいのが**セルのStyle**についてです。

```swift
super.init(style: style, reuseIdentifier: reuseIdentifier)
```

Appleはいくつかのセルのスタイルを提供しており、`init()`メソッドの第１引数に指定をしてあげることでそのスタイルを変更することが出来ます。
スタイルの種類は[こちらのドキュメント](https://developer.apple.com/documentation/uikit/uitableviewcell/cellstyle)を参照すると４種類あり、それぞれ以下のように指定してあげることが出来ます。

```swift
super.init(style: .default, reuseIdentifier: reuseIdentifier)
super.init(style: .value1, reuseIdentifier: reuseIdentifier)
super.init(style: .value2, reuseIdentifier: reuseIdentifier)
super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
```

## TableViewにUITableViewCellクラスを紐づける

作成したUITableViewCellクラスをtableViewに紐づけましょう。
紐づけは、`UITableView`クラスの`register()`メソッドを使います。

```swift
class TimelineViewController: UIViewController
{
    override func viewDidLoad() {    
        timelineView.register(ReportCell.self, forCellReuseIdentifier: "ReportCell")
    }
```

`register()`メソッドの第１引数には作成したUITableViewCellクラスを渡してあげます。
第２引数には、再利用のためにString型でカスタムセルクラスに対する名前を与えてあげます。
今回はややこしくなるのでクラス名と同じにしました。
もっと詳しいことは[公式ドキュメント](https://developer.apple.com/documentation/uikit/uitableview/1614888-register)を参照してください。

これを忘れるといくらセルをカスタマイズしても変更されませんので注意してください。

## プロパティに表示したい内容をぶち込む

表示するセルの設定も終わり、表示したいものも出揃ったところでようやくセルにぶち込んできます。

UITableViewCellクラスは、以下の３つのプロパティを用意しており、これに代入することでセルのStyleに合わせて表示をしてくれます。

```swift
var textLabel: UILabel? //メインの内容を表示できる
var detailTextLabel: UILabel? //サブコンテンツの表示に使える
var imageView: UIImageView? //サムネの表示とかはこれが使える
```

他にも`contentView`や`backgroundView`などがありますがとりあえず上の３つさえ使えばそれっぽくなります(?)

```swift
extension TimelineViewController: UITableViewDataSource
{

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let reportCell = tableView.dequeueReusableCell(withIdentifier: "ReportCell", for: indexPath)
        let date = self.postedReports[indexPath.row].createdAt.dateValue()
        
        reportCell.textLabel?.text = self.postedReports[indexPath.row].content
        reportCell.detailTextLabel?.text = formatter.string(from: date as Date)
        return reportCell
    }
```