I am trying to create custom cell for messageKi, following instruction but have an error:
Thread 1: "Invalid update: invalid number of sections. The number of sections contained in the collection view after the update (1) must be equal to the number of sections contained in the collection view before the update (1), plus or minus the number of sections inserted or deleted (1 inserted, 0 deleted). Collection view: <MessageKit.MessagesCollectionView: 0x13588b000; baseClass = UICollectionView; frame = (0 0; 0 0); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x6000017dda70>; backgroundColor = <UIDynamicCatalogColor: 0x600003a3c410; name = collectionViewBackground>; layer = <CALayer: 0x600001906220>; contentOffset: {0, 0}; contentSize: {0, 0}; adjustedContentInset: {0, 0, 0, 0}; layout: <MesKit.MyCustomMessagesFlowLayout: 0x133b27370>; dataSource: (null)>"
There is my full code:
import UIKit
import MessageKit
import InputBarAccessoryView
struct UserSender: SenderType {
var senderId: String
var displayName: String
}
struct MessageChat: MessageType {
var sender: MessageKit.SenderType
var messageId: String
var sentDate: Date
var kind: MessageKit.MessageKind
}
class ChatViewController: MessagesViewController {
lazy var messageList: [MessageChat] = []
// MARK: Injections
var currentUser = UserSender(senderId: "0", displayName: "Cur")
var notCurrentUser = UserSender(senderId: "1", displayName: "NotCur")
// MARK: View lifeCycle
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
configureMessageCollectionView()
configureMessageInputBar()
messagesCollectionView = MessagesCollectionView(frame: .zero, collectionViewLayout: MyCustomMessagesFlowLayout())
messagesCollectionView.register(MyCustomCell.self)
if let layout = messagesCollectionView.collectionViewLayout as? MessagesCollectionViewFlowLayout {
layout.textMessageSizeCalculator.outgoingAvatarSize = .zero
layout.textMessageSizeCalculator.incomingAvatarSize = .zero
layout.setMessageIncomingAvatarSize(.zero)
}
}
override open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let messagesDataSource = messagesCollectionView.messagesDataSource else {
fatalError("Ouch. nil data source for messages")
}
//before checking the messages check if section is reserved for typing otherwise it will cause IndexOutOfBounds error
if isSectionReservedForTypingIndicator(indexPath.section){
return super.collectionView(collectionView, cellForItemAt: indexPath)
}
let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
if case .custom = message.kind {
let cell = messagesCollectionView.dequeueReusableCell(MyCustomCell.self, for: indexPath)
cell.configure(with: message, at: indexPath, and: messagesCollectionView)
return cell
}
return super.collectionView(collectionView, cellForItemAt: indexPath)
}
func configureMessageCollectionView() {
messagesCollectionView.messagesDataSource = self
messagesCollectionView.messageCellDelegate = self
messagesCollectionView.messagesLayoutDelegate = self
messagesCollectionView.messagesDisplayDelegate = self
scrollsToLastItemOnKeyboardBeginsEditing = true
maintainPositionOnInputBarHeightChanged = true
showMessageTimestampOnSwipeLeft = true
}
func configureMessageInputBar() {
messageInputBar.delegate = self
messageInputBar.inputTextView.tintColor = .cyan
messageInputBar.sendButton.setTitleColor(.purple, for: .normal)
messageInputBar.sendButton.setTitleColor(
.purple,
for: .highlighted)
}
func textCell(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UICollectionViewCell? {
nil
}
func insertMessage(_ message: MessageChat) {
messageList.append(message)
messagesCollectionView.performBatchUpdates({
messagesCollectionView.insertSections([messageList.count - 1])
if messageList.count >= 2 {
messagesCollectionView.reloadSections([messageList.count - 2])
}
}, completion: { [weak self] _ in
if self?.isLastSectionVisible() == true {
self?.messagesCollectionView.scrollToLastItem(animated: true)
}
})
}
func isLastSectionVisible() -> Bool {
guard !messageList.isEmpty else { return false }
let lastIndexPath = IndexPath(item: 0, section: messageList.count - 1)
return messagesCollectionView.indexPathsForVisibleItems.contains(lastIndexPath)
}
func setTypingIndicatorViewHidden(_ isHidden: Bool, message: MessageChat? = nil, performUpdates updates: (() -> Void)? = nil) {
setTypingIndicatorViewHidden(isHidden, animated: true, whilePerforming: updates) { [weak self] success in
if success {
if message != nil {
self?.messageList.append(message!)
}
self?.messagesCollectionView.reloadData()
}
}
}
}
extension ChatViewController: MessagesDataSource {
var currentSender: MessageKit.SenderType {
currentUser
}
func messageForItem(
at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) -> MessageType {
messagesCollectionView.layer.zPosition = 9
if messageList.count == indexPath.section && messageList.count > 0 && indexPath.section > 0 {
return messageList[indexPath.section-1]
}
return messageList[indexPath.section]
}
func numberOfSections(in messagesCollectionView: MessageKit.MessagesCollectionView) -> Int {
messageList.count
}
}
extension ChatViewController: MessagesDisplayDelegate {
func backgroundColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor {
let item = messageList[indexPath.section]
if item.sender.senderId == currentUser.senderId {
return .purple
}
return .gray
}
func textColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor {
.black
}
}
extension ChatViewController: InputBarAccessoryViewDelegate {
func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith text: String) {
inputBar.invalidatePlugins()
inputBar.sendButton.startAnimating()
inputBar.inputTextView.placeholder = "Sending..."
inputBar.inputTextView.resignFirstResponder()
DispatchQueue.global(qos: .default).async {
sleep(1)
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
inputBar.sendButton.stopAnimating()
inputBar.inputTextView.placeholder = "Aa"
inputBar.inputTextView.text = ""
self.insertMessage(self.createCustomMessage(text: text))
self.messagesCollectionView.scrollToLastItem(animated: true)
}
}
}
private func createMessage(isMe: Bool, text: String) -> MessageChat {
return MessageChat(
sender: isMe ? currentUser : notCurrentUser,
messageId: UUID().uuidString,
sentDate: Date(),
kind: .text(text)
)
}
private func createCustomMessage(text: String) -> MessageChat {
return MessageChat(sender: currentUser, messageId: UUID().uuidString, sentDate: Date(), kind: .custom([text, text, text, text]))
}
}
// MARK: - ChatPresenterOutput
extension ChatViewController {
private func insertMessages(_ data: [Any]) {
for component in data {
let user = self.currentUser
if let str = component as? String {
let message = MessageChat(sender: user, messageId: UUID().uuidString, sentDate: Date(), kind: .text(str))
insertMessage(message)
}
}
}
}
open class MyCustomCell: UICollectionViewCell {
open func configure(with message: MessageType, at indexPath: IndexPath, and messagesCollectionView: MessagesCollectionView) {
self.contentView.backgroundColor = UIColor.red
}
}
open class CustomMessageSizeCalculator: MessageSizeCalculator {
open override func messageContainerSize(for _: MessageType, at _: IndexPath) -> CGSize {
return CGSize(width: 300, height: 130)
}
}
open class MyCustomMessagesFlowLayout: MessagesCollectionViewFlowLayout {
lazy open var customMessageSizeCalculator = CustomMessageSizeCalculator(layout: self)
override open func cellSizeCalculatorForItem(at indexPath: IndexPath) -> CellSizeCalculator {
if isSectionReservedForTypingIndicator(indexPath.section) {
return typingIndicatorSizeCalculator
}
let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
if case .custom = message.kind {
return customMessageSizeCalculator
}
return super.cellSizeCalculatorForItem(at: indexPath);
}
}
How can i avoid that error?
Tried this but it does not work for me: https://github.com/MessageKit/MessageKit/issues/1788