Messagekit: Support for UIContextMenuInteraction

Created on 3 Jul 2020  路  2Comments  路  Source: MessageKit/MessageKit

Hi, first let me thank everyone for the amazing library !

I've been trying all day to make this work and couldn't so I'd like to know if anyone got UIContextMenuInteraction working with MessageKit ?


Heres what I tried to do and the errors I've encountered when I tried implementing it :

1. Using cellForItemAt I add the interaction to the cell :


Code sample

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = super.collectionView(collectionView, cellForItemAt: indexPath)
    let menuInteraction = UIContextMenuInteraction.init(delegate: self)
    cell.addInteraction(menuInteraction)
    return cell
}

Errors:

  • The "preview" is for the whole cell not just the messageContainerView.
  • If I add the interaction directly to messageContainerView it simply doesn't work.
    (I'm guessing the touch just doesn't reach it)

2. Implementing UIContextMenuInteractionDelegate :


Code sample

extension ViewController: UIContextMenuInteractionDelegate {
    func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
        guard let index = self.messagesCollectionView.indexPathForItem(at: location) else {
            return nil
        }

        let message = self.messageForItem(at: index, in: self.messagesCollectionView)
        let shareAction = UIAction.init(title: "Share",
                                        image: nil,
                                        identifier: UIAction.Identifier.init("msg.share"),
                                        discoverabilityTitle: nil,
                                        attributes: [],
                                        state: .off) { (_) in
            print("Share message here")
        }
        let menu = UIMenu.init(title: "",
                                image: nil,
                                identifier: .share,
                                options: .displayInline,
                                children: [copyAction])
        let actionProvider: UIContextMenuActionProvider = { [weak self] (_) -> UIMenu? in
            return menu
        }

        return UIContextMenuConfiguration.init(identifier: "msg.share" as NSCopying, previewProvider: nil, actionProvider: actionProvider)
    }
}

Error:

  • indexPathForItem(at:) doesn't seem to return the correct index so I can't get the message content to use in UIAction
question

Most helpful comment

Hey @the0neyouseek ! You can use some methods from UICollectionViewDelegate for this. Add something like this to your MessagesViewController subclass:


    func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {

        let message = messageList[indexPath.section] // Use whatever your backing store for messages is, in the example app its an array called messageList

        return UIContextMenuConfiguration(identifier: message.messageId as NSCopying, previewProvider: nil) { suggestedActions in

            // Create an action
            let share = UIAction(title: "Share", image: nil) { action in
                print("Sharing")
            }

            return UIMenu(title: "", children: [share])
        }
    }

    func collectionView(_ collectionView: UICollectionView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
        guard let identifier = configuration.identifier as? String,
              let section = messageList.firstIndex(where: { $0.messageId == identifier }),
              let cell = collectionView.cellForItem(at: IndexPath(row: 0, section: section)) as? MessageContentCell else {
                return nil
        }

        let parameters = UIPreviewParameters()
        parameters.backgroundColor = cell.messageContainerView.backgroundColor

        return UITargetedPreview(view: cell.messageContainerView, parameters: parameters)
    }

This creates UITargetedPreview using the cell's messageContainerView so you won't get the entire cell in your preview. Plus you don't have to override any methods or adding UIActions to your cells or anything 馃槉

All 2 comments

Hey @the0neyouseek ! You can use some methods from UICollectionViewDelegate for this. Add something like this to your MessagesViewController subclass:


    func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {

        let message = messageList[indexPath.section] // Use whatever your backing store for messages is, in the example app its an array called messageList

        return UIContextMenuConfiguration(identifier: message.messageId as NSCopying, previewProvider: nil) { suggestedActions in

            // Create an action
            let share = UIAction(title: "Share", image: nil) { action in
                print("Sharing")
            }

            return UIMenu(title: "", children: [share])
        }
    }

    func collectionView(_ collectionView: UICollectionView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
        guard let identifier = configuration.identifier as? String,
              let section = messageList.firstIndex(where: { $0.messageId == identifier }),
              let cell = collectionView.cellForItem(at: IndexPath(row: 0, section: section)) as? MessageContentCell else {
                return nil
        }

        let parameters = UIPreviewParameters()
        parameters.backgroundColor = cell.messageContainerView.backgroundColor

        return UITargetedPreview(view: cell.messageContainerView, parameters: parameters)
    }

This creates UITargetedPreview using the cell's messageContainerView so you won't get the entire cell in your preview. Plus you don't have to override any methods or adding UIActions to your cells or anything 馃槉

Thanks for the help @kinoroy !
I should have thought there would already be a method somewhere, it works now 馃憤

Ps. If I may add something, I also had to set the visiblePath for the cell rounded corners to work:

let parameters = UIPreviewParameters()
parameters.backgroundColor = cell.messageContainerView.backgroundColor
+ parameters.visiblePath = UIBezierPath.init(roundedRect: cell.messageContainerView.bounds, cornerRadius: 16)
Was this page helpful?
0 / 5 - 0 ratings

Related issues

NiklasWilson picture NiklasWilson  路  4Comments

omaralbeik picture omaralbeik  路  4Comments

NiklasWilson picture NiklasWilson  路  4Comments

yspreen picture yspreen  路  3Comments

JulienLevallois picture JulienLevallois  路  4Comments