iOS
Swift
MessageKit

Message Kitの使い方まとめ

iosでチャット画面を作る際、JSQMessagesViewControllerがdeprecatedになって絶望していた。
そんな中、ついに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使って頑張ってください。

メッセージオブジェクトの作成

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

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などをセット。

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 メッセージの下の文字 ×
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 ×
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の文字が変わった時に発火 ×

最後に

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