MessageKit でLineのように長押しでメッセージを削除
UICollectionViewの機能を利用する。
削除以外の他の処理も実装可能です。
継承クラスにメニューオブジェクトを定義
MessageChatViewControllerでチャット画面の実装、ChatMessageCellでテキスト、ChatPhotoCellで画像を表示しているものとする。
class MessageChatViewController: MessagesViewController {
// 新しくロングタップメニューを追加する
let messageMenuItem = UIMenuItem(title: "削除", action: #selector(ChatMessageCell.deleteMessageData(_:)))
let photoMenuItem = UIMenuItem(title: "削除", action: #selector(ChatPhotoCell.deletePhotoData(_:)))
}
*ここはなぜかセルのメソッドでないと動かないっぽい
長押しの許可
ここから先はUICollectionViewDelegate, UICollectionViewFlowDelegateのメソッドでMessageChatViewControllerクラスに実装する。
// 長押しを許可するか?
override func collectionView(_ collectionView: UICollectionView, shouldShowMenuForItemAt indexPath: IndexPath) -> Bool {
// 自分のメッセージのみ
let message = self.messageForItem(at: indexPath, in: messagesCollectionView)
if message.sender.senderId != currentSender().senderId {
return false
}
// セル種別
if let _ = collectionView.cellForItem(at: indexPath) as? ChatMessageCell {
return true
}
if let _ = collectionView.cellForItem(at: indexPath) as? ChatPhotoCell {
return true
}
return false
}
メニュー選択時の処理の実行を許可
ちょっと面倒だけど編集時に編集を許可するとかそんな設計思考ににてる。
actionでメニュー選択時に実行する処理が渡されるのでdescriptionプロパティ でセレクタを判断してUIMenuItem生成時に設定したメソッドであればtrueを返す。
// 長押しメニューを選択時に処理の実行を可能にするか?
override func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
// セル種別
if let _ = collectionView.cellForItem(at: indexPath) as? ChatMessageCell {
// 削除のみ
if action.description == "deleteMessageData:" {
return true
}
}
if let _ = collectionView.cellForItem(at: indexPath) as? ChatPhotoCell {
// 削除のみ
if action.description == "deletePhotoData:" {
return true
}
}
return false
}
こここでよくあるように「return super.collectionView(collectionView, canPerformAction: action, forItemAt: indexPath, withSender: sender)」を入れておくとコピーとかそんなデフォルトの動きもメニューに追加される。
長押しメニュー選択時の処理
ここまできてようやく実際の処理を記述する。
便利なのはindexPathとactionが渡されること。
長押しメニューで選択したときに自力で変数を渡す処理を書かなくていい。
// 長押しメニュー実行時の処理 delegeteでセルから戻ってくる
override func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) {
let message = self.messageForItem(at: indexPath, in: messagesCollectionView)
guard let message_id = Int(message.messageId) else { return }
// セル種別
if let _ = collectionView.cellForItem(at: indexPath) as? ChatMessageCell {
// 削除のみ
if action.description == "deleteMessageData:" {
// テキストセル削除処理
}
}
if let _ = collectionView.cellForItem(at: indexPath) as? ChatPhotoCell {
// 削除のみ
if action.description == "deletePhotoData:" {
//画像セル削除処理
}
}
}
セルにメソッドを追加
長押しメニューからの処理はセル→UICollectionViewのコールバックで行われます。UIMenuItem生成時に設定するメソッドの作成と、そのメソッドからUICollectionViewの処理を呼び出す部分を定義します。
// メッセージ削除処理 delegeteでコールバックして実行する
@objc func deleteMessageData(_ sender: Any?) {
if let collectionView = self.superview as? UICollectionView {
// indexPath取得
if let indexPath = collectionView.indexPath(for: self) {
// action実行
collectionView.delegate?.collectionView?(collectionView, performAction: NSSelectorFromString("deleteMessageData:"), forItemAt: indexPath, withSender: sender)
}
}
}
セルからindexPathを取得してセレクタ指定で処理を実行。
セレクタをメソッド名と同じにしてるけど、ここはおそらく別でもいけるはず。。。