Messagekit: Delete message by long press on cell ?

Created on 29 May 2018  路  16Comments  路  Source: MessageKit/MessageKit

Hi ! I want to delete message by long press gesture on cell.
Currently i can copy the message, how can i implement the possibility of add one action ?

Thanks

question

Most helpful comment

Okay, so @JulienLevallois here is a working example.

You need to
1.) implement the canPerformAction and performAction delegate methods in your subclass of MessagesViewController, like so:

 override func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool {

        if action == NSSelectorFromString("delete:") {
            return true
        } else {
            return super.collectionView(collectionView, canPerformAction: action, forItemAt: indexPath, withSender: sender)
        }
    }

override func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) {

        if action == NSSelectorFromString("delete:") {
            // 1.) Remove from datasource
            // insert your code here

            // 2.) Delete sections
            collectionView.deleteSections([indexPath.section])
        } else {
            super.collectionView(collectionView, performAction: action, forItemAt: indexPath, withSender: sender)
        }
    }

2.) Also you need to write an extension to the class of the UICollectionView subclass you are using and provide the implementation of the action there, e.g.

extension MessageCollectionViewCell {

    override open func delete(_ sender: Any?) {

        // Get the collectionView
        if let collectionView = self.superview as? UICollectionView {
            // Get indexPath
            if let indexPath = collectionView.indexPath(for: self) {
                // Trigger action
                collectionView.delegate?.collectionView?(collectionView, performAction: NSSelectorFromString("delete:"), forItemAt: indexPath, withSender: sender)
            }
        }
    }
}

That is the easiest way that I have found, I hope it helps!

Update: The code above is meant for system actions like copy or delete. In order to add custom actions, just follow the same scheme, but add your custom actions also to the UIMenuController, like so:

class CustomChatViewController: MessagesViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let customMenuItem = UIMenuItem(title: "Quote", action: #selector(MessageCollectionViewCell.quote(_:)))
        UIMenuController.shared.menuItems = [customMenuItem]
     }

}

Of course add the custom actions also to canPerformAction and performAction and to your UICollectionViewCell extension:

extension MessageCollectionViewCell {
    @objc func quote(_ sender: Any?) {

        // Get the collectionView
        if let collectionView = self.superview as? UICollectionView {
            // Get indexPath
            if let indexPath = collectionView.indexPath(for: self) {
                // Trigger action
                collectionView.delegate?.collectionView?(collectionView, performAction: #selector(MessageCollectionViewCell.quote(_:)), forItemAt: indexPath, withSender: sender)
            }
        }
    }
}

All 16 comments

I dont understand where i need to override this, and what i need to do, to have 2 buttons, copy & delete

I dont understand where i need to override this, and what i need to do, to have 2 buttons, copy & delete

In the subclass of MessagesViewController, for example for the "delete" action (pseudocode out of my head):

class MyChatViewController: MessagesViewController {

override func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool {

        if action == #selector(UIResponderStandardEditActions.delete(_:)) {
            return true
        }

        return super.canPerformAction(action, withSender: sender)

    }

override func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) {

        // Delete action should be handled by yourself
        if action == #selector(UIResponderStandardEditActions.delete(_:)) {
           // Remove entry from datasource
           // Delete section of the collectionView
           return
        }

        // Use default handler instead
        super.collectionView(collectionView, performAction: action, forItemAt: indexPath, withSender: sender)

    }

}

Okay, forget my previous comment. I just tested it and it does not seem to work. I will try to get it working (as I also need custom actions) and report back :)

Okay, so @JulienLevallois here is a working example.

You need to
1.) implement the canPerformAction and performAction delegate methods in your subclass of MessagesViewController, like so:

 override func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool {

        if action == NSSelectorFromString("delete:") {
            return true
        } else {
            return super.collectionView(collectionView, canPerformAction: action, forItemAt: indexPath, withSender: sender)
        }
    }

override func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) {

        if action == NSSelectorFromString("delete:") {
            // 1.) Remove from datasource
            // insert your code here

            // 2.) Delete sections
            collectionView.deleteSections([indexPath.section])
        } else {
            super.collectionView(collectionView, performAction: action, forItemAt: indexPath, withSender: sender)
        }
    }

2.) Also you need to write an extension to the class of the UICollectionView subclass you are using and provide the implementation of the action there, e.g.

extension MessageCollectionViewCell {

    override open func delete(_ sender: Any?) {

        // Get the collectionView
        if let collectionView = self.superview as? UICollectionView {
            // Get indexPath
            if let indexPath = collectionView.indexPath(for: self) {
                // Trigger action
                collectionView.delegate?.collectionView?(collectionView, performAction: NSSelectorFromString("delete:"), forItemAt: indexPath, withSender: sender)
            }
        }
    }
}

That is the easiest way that I have found, I hope it helps!

Update: The code above is meant for system actions like copy or delete. In order to add custom actions, just follow the same scheme, but add your custom actions also to the UIMenuController, like so:

class CustomChatViewController: MessagesViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let customMenuItem = UIMenuItem(title: "Quote", action: #selector(MessageCollectionViewCell.quote(_:)))
        UIMenuController.shared.menuItems = [customMenuItem]
     }

}

Of course add the custom actions also to canPerformAction and performAction and to your UICollectionViewCell extension:

extension MessageCollectionViewCell {
    @objc func quote(_ sender: Any?) {

        // Get the collectionView
        if let collectionView = self.superview as? UICollectionView {
            // Get indexPath
            if let indexPath = collectionView.indexPath(for: self) {
                // Trigger action
                collectionView.delegate?.collectionView?(collectionView, performAction: #selector(MessageCollectionViewCell.quote(_:)), forItemAt: indexPath, withSender: sender)
            }
        }
    }
}

This issue has been marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

@Luke47 thank you very much :)

@JulienLevallois you need to delete the message from your datasource as he commented in the code.

Sent with GitHawk

Glad this one could be resolved! Thanks everyone 馃槂

Sent with GitHawk

Hi @Luke47 & @SD10 , " delete " messages dont works for Location type message. For text & images its good but long press dont works for location :/ I want to be able to delete a location message.

Do you have ideas why ?
Thanks

@kpsdeveloper You'll have to look at my comment above in this thread. This is a general iOS question and not really related to MessageKit 馃槩

@Luke47 I tried your answer, but my i couldn't get my delete action to do anything, not even a print statement. in other words, the delete action isn't called but it shows with the copy. any idea what might be wrong?

Okay this works, the issue i have with this now is in this func messageForItem(at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageType { return messageList[indexPath.section] } throws an index out of range error. How can I fix this please?

@tobitech Have you forgotten to call delete on the index path of the collection view while also removing it from your messages array?

Sent with GitHawk

@nathantannar4 I did
messageList.remove(at: indexPath.section) collectionView.deleteSections([indexPath.section]) messagesCollectionView.reloadData()
the crash only occurs when i'm deleting the last item in my messages array

Hi, solution Luke gave seems like for deleting message from both sides. I need to implement deleting for only user who deletes it. So other person can still see the message like in whatsapp.
What changes should i make, as person leaves chat room and comes back this message will load again as its not deleted from database right? I need to implement a filtering method. Don't know how.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

NiklasWilson picture NiklasWilson  路  4Comments

Abacaxi-Nelson picture Abacaxi-Nelson  路  4Comments

TheDarkCode picture TheDarkCode  路  3Comments

bilaalrashid picture bilaalrashid  路  3Comments

brandon-haugen picture brandon-haugen  路  3Comments