【Swift】UITableViewでTwitterのタイムラインを実装してみた（その３）

本記事は、
[【Swift】UITableViewでTwitterのタイムラインを実装してみた（その２）](https://qiita.com/Kai_82/items/26ab28296b81f93101e6)
の続きになります。

その２の記事をご覧になっていない方は是非そちらもご覧ください!
GitHubリポジトリは[**コチラ**](https://github.com/onishi-app/SampleTableView)になります。

では前回の続きになります。

***
前回はある程度の機能を実装しましたが幾つか課題があったと思います。
今回はその実装をしていきます。

課題としては下記があげられました。
**・投稿時間の部分が適切な記載になっていない。**
　-> 本来はhh : mm : ss YYYY / MM / ddの表記
　-> 全てのツイートが同じ時刻になっている
**・いいね、リツイートなどの値も全て同じ
・リプライがない場合は下の部分をグレー表示にする
・右上のオプションボタン(・・・)がない。**
　-> 押すとブロックや非表示だできるやつ
**・リプライの内容を実装していない**

では、一つずつ実装していきます。

まずは各ツイートに時間やいいね、リツイートの情報を持たせます。
今回はTweetの内容が格納されている型を作成してその型を配列にしようと思います。

TweetModel型を定義します。

ツイートの内容やいいね、リツイートの数を保持するためのプロパティと、
ツイート時の時間を`HH:mm:ss yyyy/MM/dd`の
形式で表示するためのメソッドを定義しました。

```swift:TweetModel.swift

import Foundation

struct TweetModel {
    var name: String
    var content: String
    var time: TimeInterval
    var good: Double
    var retweet: Double
    var quoteRetweet: Double
    
    func chengeDateFormatter(time: TimeInterval) -> String {
        let date = Date(timeIntervalSince1970: time)
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "HH:mm:ss yyyy/MM/dd"
        
        let dateString = dateFormatter.string(from: date)
        return dateString
    }
}
```

配列itemに修正を加え、TweetModel型を格納した配列に変更します。

```swift:ViewController.swift

    var tweetArray = [
        TweetModel(name: "山田一郎", content: "今日はいい天気です。午前中に洗濯を干したいと思います。", time: Date().timeIntervalSince1970 - 12345678, good: 10, retweet: 2, quoteRetweet: 1),
        TweetModel(name: "山田二郎", content: "おはようございます。", time: Date().timeIntervalSince1970 - 1234567, good: 1000, retweet: 10, quoteRetweet: 7),
        TweetModel(name: "山田三郎", content: "朝ごはんは目玉焼きとパンです。", time: Date().timeIntervalSince1970 - 123456, good: 20392, retweet: 1232, quoteRetweet: 1221),
        TweetModel(name: "佐藤健", content: "映画「るろうに剣心」よろしくお願いします。", time: Date().timeIntervalSince1970 - 12345, good: 100022, retweet: 12911, quoteRetweet: 1292),
        TweetModel(name: "新垣結衣", content: "逃げるは恥だが役にたつ", time: Date().timeIntervalSince1970 - 1234, good: 1212121, retweet: 21321, quoteRetweet: 12131),
        TweetModel(name: "夏目漱石", content: "吾輩は猫である", time: Date().timeIntervalSince1970 - 123, good: 1200, retweet: 12, quoteRetweet: 12),
        TweetModel(name: "李朴", content: "なるべくしてなっている", time: Date().timeIntervalSince1970 - 12, good: 2112, retweet: 2112, quoteRetweet: 2112),
        TweetModel(name: "呉鳳明", content: "すりつぶせ", time: Date().timeIntervalSince1970 - 1, good: 1000000, retweet: 100000, quoteRetweet: 100000)
    ]
```

他には`cellForRowAt`や`didSelectRowAt`内に記述されている配列の参照の処理を、
itemからtweetArrayを参照するように変更しました。

これで各ツイートにそれぞれの情報を持たせることができました。

次に実装する機能は「**リプライがない場合は下の部分をグレー表示にする**」です。

表示のないセルをグレー表示にするのは実は簡単で、
TableViewのstyleを変更することで実装できます。

`.plain`だったのを`.grouped`に変更しました。

```swift:DetailViewController.swift

let tableView = UITableView(frame: self.view.bounds, style: .grouped)
```

この時点でのシミュレータは次のようになります。
![スクリーンショット 2021-01-02 8.57.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/e0d324c9-5ba8-d54d-de0f-8aac5f0577e3.png)


ぱっと見は大丈夫そうですが一つ問題点がございます。
**ツイートの上にグレーの隙間が存在してしまっています。**

上にグレーの隙間があることは望まない結果なのでこちらの対処を行います。

遭遇したことのない事象でしたのでいろいろ調べてみました。
どうやら下記の二つが必要ですので追加してみてください！

どちらか一つでは変化がありませんでした。

`sectionHeaderHeight`はTableViewのヘッダーの高さを変更します。
0を指定すると他のオブジェクトの影響を受けてしまうため0.1にしました。

`viewForHeaderInSection`は、UIViewを返しそれをヘッダーに表示するのですが、
何も指定せずにUIView()を返すことでヘッダーをなくしています。

```swift:ViewController.swift

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.sectionHeaderHeight = 0.1
    }

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return UIView()
    }
```

これらの記述により次のような状態になります。
![スクリーンショット 2021-01-02 9.12.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/9ceb7e23-9015-0da6-db23-f2301bf748ef.png)

次に実装する機能は「**右上のオプションボタン(・・・)がない**」です。

まずはUIの設計を行います。
![スクリーンショット 2021-01-02 9.23.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/dfcb4e74-76c4-9997-c6c5-ea27a9b5c4c9.png)

カスタムセルの右上に新しくボタンを追加しました。
このボタンが押された時の処理をカスタムセルの方のファイルに書いていきます。

今回は`print()`でログに出力するだけにしておこうと思います。

次に実装する機能は「**リプライの内容を実装していない**」に対しての実装です。

`TweetModel型`の内容を少し変更します。
`var reply: [TweetModel]`を追加しました。
リプライがある場合はこの配列replyの中にTweetModel型の要素を格納していきます。

```swift:TweetModel.swift

    var name: String
    var content: String
    var time: TimeInterval
    var good: Double
    var retweet: Double
    var quoteRetweet: Double
    var reply: [TweetModel]
```

型にプロパティを追加したのでtweetArrayの内容も変わります。

最後に`reply: []`が追加されています。
リプライがある場合はここに値が入っていきます。

```swift:ViewController.swift

    var tweetArray = [
        TweetModel(name: "山田一郎",
                   content: "今日はいい天気です。午前中に洗濯を干したいと思います。",
                   time: Date().timeIntervalSince1970 - 12345678,
                   good: 10,
                   retweet: 2,
                   quoteRetweet: 1,
                   reply: [])
    ]
```

今回はviewDidLoad内で追加していきます。

```swift:ViewController.swift

    override func viewDidLoad() {
        super.viewDidLoad()
        let reply1 = TweetModel(name: "山田二郎", content: "本当ですよね。", time: Date().timeIntervalSince1970 - 1234567, good: 11, retweet: 1, quoteRetweet: 2, reply: [])
        let reply2 = TweetModel(name: "山田三郎", content: "こっちは雨です。", time: Date().timeIntervalSince1970 - 12345634, good: 12, retweet: 4, quoteRetweet: 2, reply: [])
        let reply3 = TweetModel(name: "麃公", content: "バハァ！！！", time: Date().timeIntervalSince1970 - 5, good: 120031020, retweet: 1032000, quoteRetweet: 1122, reply: [])
        let reply4 = TweetModel(name: "王騎", content: "天下の大将軍ですよぉ", time: Date().timeIntervalSince1970 - 1, good: 1223456543, retweet: 123123, quoteRetweet: 123123, reply: [])
        tweetArray[0].reply.append(reply1)
        tweetArray[0].reply.append(reply2)
        tweetArray[7].reply.append(reply3)
        tweetArray[7].reply[0].reply.append(reply4)
    }
```

今までの`didSelectRowAt`では
DetailViewController.swiftのreplyプロパティに値を渡していないので、
配列内のreplyの値をDetailVCのreplyに渡す処理を追記します。

```swift:ViewController.swift

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let detailVC = storyboard?.instantiateViewController(identifier: "detailView") as! DetailViewController

        detailVC.reply = tweetArray[indexPath.row].reply
        
        navigationController?.pushViewController(detailVC, animated: true)
    }
```

DetailViewController.swiftで下記の宣言があります。
`var reply: [TweetModel] = []`

このreplyプロパティにタップしたセルに対してのリプライが格納されています。

セルをタップ後の画面でもTableViewを構成するわけですが、
最初のセルから４番目のセルまではタップしたツイートの詳細が表示され、
５番目以降がリプライ情報になります。

リプライはデフォルトケースで実装されるのでデフォルトケース内を修正します。

`let past = reply[count - 4].time`で
リプライの時間をTimeinterval型で取得しています。

`let now = Date().timeIntervalSince1970`で現在の時刻を取得し、
`cell.time.text = reply[count - 4].timeCheck(now: now, past: past)`で
現在の時刻と投稿時の時刻の差分をチェックしています。

`reply[count - 4]`は、
リプライを読み込む際のcountの値が最低でも４なのでそこから４を引くことで
配列０番目から読み込むことが可能になっています。

```swift:DetailViewController.swift

        default:
            tableView.register(UINib(nibName: "CustomTableViewCell", bundle: nil), forCellReuseIdentifier: "customCell")
            let cell = tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! CustomTableViewCell
            let past = reply[count - 4].time
            let now = Date().timeIntervalSince1970
            cell.name.text = reply[count - 4].name
            cell.time.text = reply[count - 4].timeCheck(now: now, past: past)
            cell.content.text = reply[count - 4].content
            count += 1
            return cell
        }
```

これらの処理を実装した後の実際の挙動は次のようになります。
![スクリーンショット 2021-01-02 12.18.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/727104f8-7c9f-0729-a663-e254a4564325.png)

しっかりとリプライが表示されていることが確認できました。

以上でタイムラインの実装を終了にしたいと思います。

まだまだいろいろと実装できることはありますが、
実装しはじめたら終わらないのでこのくらいにしたいと思います。

最後までご覧いただきありがとうございました！