Swift チャットアプリのチャット画面が作りたい！

#チャットアプリのチャット画面に必要なもの
コロナ禍の中なかなか就職が決まらず暇なので、チャットアプリのチャット画面を作り方を記事にしたいと思います。
チャット画面に最低限必要なものってなんだと思いますか？
今回は僕が勝手に思う`チャット画面に最低限必要なもの`で実装していきます。
必要なコンポーネントは以下
>tableView: メッセージを一覧表示するのに使います。
MessageCell: プロフィールとかメッセージをまとめます。
profileImageView: プロフィール画像を表示をします。
messageLabel: メッセージの表示をします。

他にも送信時間表示用のlabelとかは？と思われると思いますが、データベースとか使って実際にメッセージの送受信をするわけではないので今回はこれだけにします。

#実装
まずはViewControllerの方から

```swift
class ViewController: UIViewController {

    private let tableView = UITableView()
    
    //メッセージを格納する用の配列
    private var messages = [String]()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        initTableView()
        initMessages()
        view.addSubview(tableView)
    }

    //tableViewの設定
    private func initTableView() {
        tableView.tableFooterView = UIView()
        tableView.separatorStyle = .none
        tableView.register(MessageCell.self, forCellReuseIdentifier: MessageCell.id)
        tableView.delegate = self
        tableView.dataSource = self
        tableView.frame = view.bounds
    }
    
    //messagesにメッセージを追加
    private func initMessages() {
        messages.append("こんにちは")
        messages.append("今日の12時ごろ予定空いてますか？")
        messages.append("ひまですね")
        messages.append("寿限無(じゅげむ)　寿限無(じゅげむ)　五劫(ごこう)のすりきれ　海砂利(かいじゃり)水魚(すいぎょ)の水行末(すいぎょうまつ) 雲来末(うんらいまつ) 風来末(ふうらいまつ)　食(く)う寝(ね)るところに　住(す)むところ　やぶらこうじの　ぶらこうじ　パイポ　パイポ　パイポの　シューリンガン　シューリンガンの　グーリンダイ　グーリンダイの　ポンポコピーのポンポコナの　長久命(ちょうきゅうめい)の長助(ちょうすけ)")
    }

}

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return messages.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: MessageCell.id, for: indexPath) as! MessageCell
        cell.message = messages[indexPath.row]
        return cell
    }
}

```

`initTableView()`の中を見ていきましょう。
`tableFooterView = UIView()`は要素数以上の時に表示される空のcellを消すためです。
`separatorStyle = .none`はcellとcellの間の薄い灰色のボーダー線を消します。
`register`はカスタムセルを表示するためです。
こちらを参照してください。
[コードでカスタムセルを使う時register(_:forCellReuseIdentifier:)関数を入れそびれていた](https://qiita.com/nakagawa1017/items/1d2c37982bcc9692c402)
ViewControllerの中についての説明はこれくらいですかね。

次はカスタムセルを作っていきます。

```swift:Message.swift
class MessageCell: UITableViewCell {
    
    static let id = "MessageCell"
    
    public var message: String? {
        didSet {
            messageLabel.text = message
        }
    }
    
    private let profileImageView: UIImageView = {
        let imageView = UIImageView()
        imageView.image = #imageLiteral(resourceName: "image.jpeg")
        imageView.layer.masksToBounds = true
        imageView.layer.cornerRadius = 25
        return imageView
    }()
    private let backView: UIView = {
        let view = UIView()
        view.backgroundColor = .opaqueSeparator
        view.layer.cornerRadius = 20
        view.layer.masksToBounds = true
        return view
    }()
    private let messageLabel: UILabel = {
        let label = UILabel()
        label.font = .systemFont(ofSize: 16)
        // labelの行数を指定
        label.numberOfLines = 0
        return label
    }()

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        addSubview(profileImageView)
        addSubview(backView)
        backView.addSubview(messageLabel)
        
        setupAutoLayout()
    }
    
    private func setupAutoLayout() {
        profileImageView.translatesAutoresizingMaskIntoConstraints = false
        backView.translatesAutoresizingMaskIntoConstraints = false
        messageLabel.translatesAutoresizingMaskIntoConstraints = false
        
        let constraints = [
            profileImageView.topAnchor.constraint(equalTo: topAnchor, constant: 10),
            profileImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10),
//            profileImageView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10),
            profileImageView.widthAnchor.constraint(equalToConstant: 50),
            profileImageView.heightAnchor.constraint(equalToConstant: 50),
            
            backView.topAnchor.constraint(equalTo: profileImageView.topAnchor),
            backView.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 5),
            backView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10),
            backView.widthAnchor.constraint(lessThanOrEqualToConstant: 250),
            
            messageLabel.topAnchor.constraint(equalTo: backView.topAnchor, constant: 8),
            messageLabel.leadingAnchor.constraint(equalTo: backView.leadingAnchor, constant: 8),
            messageLabel.bottomAnchor.constraint(equalTo: backView.bottomAnchor, constant: -8),
            messageLabel.trailingAnchor.constraint(equalTo: backView.trailingAnchor, constant: -8),
        ]
        
        NSLayoutConstraint.activate(constraints)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}
```

`profileImageView`と`backView`と`messageLabel`の宣言をしています。
クロージャでまとめるといいですね。
`backView`は`messageLabel`に直接背景色を指定してしまうと背景と文字の間に余白がなくなって見ずらいのでいい感じに余白を作るために使います。
`setupAutoLayout `の中を見ていきましょう。
各`view`に`translatesAutoresizingMaskIntoConstraints ＝ false`という記述があります。
これをしないと`autolayout`が使えないことがあるらしいです。
[Auto Layoutをコードから使おう](https://blog.personal-factory.com/2016/01/11/make-auto-layout-via-code/)
`constraints`からは`autoLayout`でレイアウトを設定しています。
配列でまとめているので`NSLayoutConstraint.activate()`でまとめて有効にできます。
一部分だけ変更したい場合は

```swift
[viewの変数名].topAnchor.constraint(equalTo: topAnchor).isActive = true
```
でできます。

ビルドしてみましょう。
![Simulator Screen Shot - iPhone 11 - 2021-01-17 at 22.33.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567404/da1afd1e-ec58-2f63-97bb-ac443dcfcba4.png)
いい感じに表示できてますね。

#まとめ
今回は相手側だけになりましたが制約を変更すれば自分の方も作れます。
もっと簡単にできる方法があれば教えてください。
わざわざ自分で実装するのめんどくさいという方は`MessageKit`を調べてみてください。

冒頭でも言ったように就職先募集してます。
あー、優しい企業様就職先をください！