Message Kitの使い方まとめ

iosでチャット画面を作る際、[JSQMessagesViewController](https://github.com/jessesquires/JSQMessagesViewController)がdeprecatedになって絶望していた。
そんな中、ついに[MessageKit](https://github.com/MessageKit/MessageKit)のver1.0.0が出たが、ドキュメントなし。毎回コードとにらめっこして実装するのは地獄なので軽くメモ。

#環境
- macOS High Sierra 10.13.4
- Xcode 9.4
- iOS 11.4
- Swift 4.1
- MessageKit 1.0.0

#導入
carthageやcocoaPod使って頑張ってください。

#メッセージオブジェクトの作成
まずはこんな感じでメッセージオブジェクトを作成。必須の要素以外にも欲しい変数を定義できるので便利。

```swift
struct Message: MessageType {

   /// 必須
   var messageId: String
   var sender: Sender
   var sentDate: Date
   var kind: MessageKind

   /// 必須ではない
   var userImagePath: String
}
```
#ViewControllerの作成
MessagesViewControllerを継承したViewControllerを作るだけでok。~~部分的にチャット画面を使いたいよーって場合はcontainerView使えば対応可能~~できないみたい( cf https://github.com/MessageKit/MessageKit/issues/737 )。そして、delegateなどをセット。

```swift
class ChatViewController: MessagesViewController {

   var messages: [Message] = []

   override func viewDidLoad() {
      super.viewDidLoad()

      messagesCollectionView.messagesDataSource = self
      messagesCollectionView.messagesLayoutDelegate = self
      messagesCollectionView.messagesDisplayDelegate = self
      messagesCollectionView.messageCellDelegate = self
      messageInputBar.delegate = self
  }
}
```
#protocolの実装
さて、ここからが少し面倒なところ。それぞれの関数を説明していきます。よく使うであろうものだけサンプルコード載せておきます。
##MessagesDataSource
| 関数名|機能|必須かどうか|
|---|---|---|
|currentSender|自分の情報を設定|○|
|numberOfSections|表示するメッセージの数|○|
|messageForItem|メッセージの実態。返り値はMessageType|○|
|cellTopLabelAttributedText|セルの上の文字|×|
|messageTopLabelAttributedText|メッセージの上の文字|×|
|messageBottomLabelAttributedText|メッセージの下の文字|×|

```swift
extension ChatViewController: MessagesDataSource {

   func currentSender() -> Sender {
      return Sender(id: "12345", displayName: "taro")
   }

   func numberOfSections(in messagesCollectionView: MessagesCollectionView) -> Int {
      return messages.count
    }

   func messageForItem(at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageType {
      return messages[indexPath.section]
    }
}
```

##MessagesLayoutDelegate
| 関数名|機能|必須かどうか|
|---|---|---|
|headerViewSize|HeaderのView Size|×|
|footerViewSize|FooterのView Size|×|
|cellTopLabelHeight|cellTopLabelAttributedTextを表示する高さ|×|
|messageTopLabelHeight|messageTopLabelAttributedTextを表示する高さ|×|
|messageBottomLabelHeight|messageBottomLabelAttributedTextを表示する高さ|×|

##MessagesDisplayDelegate
(\*は後で詳しく説明)

| 関数名|機能|必須かどうか|
|---|---|---|
|messageStyle|messageの背景スタイル、角丸とか三角のちょぼ付けるとかを指定|×|
|backgroundColor|背景色|×|
|messageHeaderView|Header View|×|
|messageFooterView|Footer View|×|
|configureAvatarView\*|Avatar View|×|
|**以下はtextの場合専用**
|textColor|テキストの色|×|
|enabledDetectors\*|自動検出してくれるものを指定|×|
|detectorAttributes|自動検出されたもののAttributes|×|

```swift
extension ChatViewController: MessagesDataSource {

   /// URLから画像をセットする場合はこのようにする。非同期にできないのが辛いところ
   func configureAvatarView(_ avatarView: AvatarView, for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) {
      let avatar: Avatar
      if let url = URL(string: messages[indexPath.section].userImagePath) {
         do {
            let imageData = try Data(contentsOf: url)
            let image = UIImage(data: imageData)
            avatar = Avatar(image: image, initials: "?")
         } catch {
            avatar = Avatar(image: nil, initials: "?")
         }
      } else {
         avatar = Avatar(image: nil, initials: "?")
      }
      avatarView.set(avatar: avatar)
   }

   /// こうしておけば、自動で電話番号やURLを検出してくれる。
   /// もちろんタップ処理も実装されている
   func enabledDetectors(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> [DetectorType] {
      return [.url, .address, .phoneNumber, .date, .transitInformation]
   }
}
```
##MessageCellDelegate
tap系はシンプル書くほどでもないので省略。`MessageLabelDelegate` を継承しているのでdetectorsのタップ処理もこの中に含まれている。

##MessageInputBarDelegate
| 関数名|機能|必須かどうか|
|---|---|---|
|messageInputBar(_:didPressSendButtonWith:)|送信ボタンが押された時|×|
|messageInputBar(_:didChangeIntrinsicContentTo:)|TextViewのサイズが変わった時に発火|×|
|messageInputBar(_:textViewTextDidChangeTo:)|TextViewの文字が変わった時に発火|×|

# 最後に
ここには書かなかったけど他にも位置情報の表示や画像の表示、送信ボタンをカスタマイズしたりボタンを追加したりも可能で便利。