Aditya Sharma
Aditya Sharma

Reputation: 605

Updating Menu Items In UIMenuController

I want to update menu items in tableview menu item controller as now I am getting these only enter image description here

I have implemented this:

func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool {
        let forword = UIMenuItem(title: "Demo", action: #selector(self.demo))
        UIMenuController.shared.menuItems?.append(forword)
        UIMenuController.shared.update()
        return true
    }
    
    func tableView(_ tableView: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) {
        UIMenuController.shared.setMenuVisible(true, animated: true)
    }
    
    func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
        return true
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        self.clearAllSelectedCell()
    }

But my requirement is to make this:

enter image description here

How can I achieve this?

Upvotes: 2

Views: 3085

Answers (3)

Harish Rathuri
Harish Rathuri

Reputation: 72

Follow the below steps, to achieve the custom items on long press.

  1. Paste the below code in viewDidload method let reply = UIMenuItem(title: "Reply", action: #selector(MessageCollectionViewCell.reply(_ :))) let edit = UIMenuItem(title: "Edit", action: #selector(MessageCollectionViewCell.edit(_ :))) let forward = UIMenuItem(title: "Forward", action: #selector(MessageCollectionViewCell.forward(_ :))) UIMenuController.shared.menuItems = [reply, edit, forward] UIMenuController.shared.update()

  2. Extension for your custom cell extension MessageCollectionViewCell {

    @objc func reply(_ 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.reply(_:)), forItemAt: indexPath, withSender: sender) } } }

    @objc func edit(_ 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.edit(_:)), forItemAt: indexPath, withSender: sender) } } }

    @objc func forward(_ 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.forward(_:)), forItemAt: indexPath, withSender: sender) } } }

}

  1. Add this code inside your controller, in my case In doing this (class ChatViewController: MessagesViewController{})

    // ---------------------------------------------------------------------------------------- // MARK: - Handle Long Press Action // ---------------------------------------------------------------------------------------- override func collectionView(_ collectionView: UICollectionView, shouldShowMenuForItemAt indexPath: IndexPath) -> Bool { return true }

    override func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool { print(action.description) let message = messages[indexPath.section] //let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)

    if action == NSSelectorFromString("reply:") {
        return true
    }
    else if action == NSSelectorFromString("edit:") {
        return true
    
    }
    else if action == NSSelectorFromString("forward:") {
        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("reply:") {
        print("reply item here!!")
    }
    else if action == NSSelectorFromString("edit:") {
        print("edit item here!!")
    }
    else if action == NSSelectorFromString("forward:") {
        print("forward item here!!")
    }
    else {
        super.collectionView(collectionView, performAction: action, forItemAt: indexPath, withSender: sender)
    }
    

    }

Upvotes: 0

Aditya Sharma
Aditya Sharma

Reputation: 605

1.  create protocol to provide control into your view controller :

public protocol MenuItemDelegate {
    func copyAction(cell: UITableViewCell)
    func forwordAction(cell: UITableViewCell)
    func deleteAction(cell: UITableViewCell)
}

2.  create a custom table cell:

        class MyTableCell: UITableViewCell {

            var menuItemDelegate: MenuItemDelegate!

            var isCopyEnable = true
            var isForwardEnable = true
            var isDeleteEnable = true


            override func awakeFromNib() {
                super.awakeFromNib()
            }

            func setUpmenu(){
                let menu = UIMenuController.shared
                let forword = UIMenuItem(title: "Forward", action: #selector(self.forword(_:)))
                let delete = UIMenuItem(title: "Delete", action: #selector(self.deleteAction(_:)))
                menu.menuItems = [forword,delete]
                if !isDeleteEnable{
                    menu.menuItems?.remove(at: (menu.menuItems?.index(of: delete))!)
                }
                if !isForwardEnable{
                    menu.menuItems?.remove(at: (menu.menuItems?.index(of: forword))!)
                }
                menu.update()
            }

            override public func copy(_ sender: Any?) {
                UIPasteboard.general.string = accessibilityValue
                menuItemDelegate.copyAction(cell: self)
                UIMenuController.shared.setMenuVisible(false, animated: true)
            }

            @objc public func forword(_ sender: Any?) {
                menuItemDelegate.forwordAction(cell: self)
                UIMenuController.shared.setMenuVisible(false, animated: true)
            }

            @objc public func deleteAction(_ sender: Any?) {
                menuItemDelegate.deleteAction(cell: self)
                UIMenuController.shared.setMenuVisible(false, animated: true)
            }

            override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
                return (action == #selector(copy(_:))  || action == #selector(forword(_:)) || action == #selector(deleteAction(_:)))
            }
    }

3. Implement in view controller as like : 


func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "MyTableCell", for: indexPath) as! MyTableCell
    cell.isForwardEnable = false
    cell.setUpmenu()
    return cell
}

func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool {
    return true
}

func tableView(_ tableView: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) {
    UIMenuController.shared.setMenuVisible(true, animated: true)
}

func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
    return true
}

It will help you to achieve this.

Upvotes: 1

Ashley Mills
Ashley Mills

Reputation: 53121

There are a few things wrong with your code…

  • Don't use UIMenuController.shared.menuItems?.append, just set menuItems
  • Don't do that in shouldShowMenuForRowAt, just do it once in viewDidLoad
  • You don't need to setMenuVisible

And most importantly

  • The action for your menu item must be a method on your table view cell subclass.

So, in your case…

Your table view controller should have these methods…

override func viewDidLoad() {
    super.viewDidLoad()

    let forward = UIMenuItem(title: "Forward", action: #selector(MyCell.menuItemTapped(_ :)))
    let delete = UIMenuItem(title: "Delete", action: #selector(MyCell.menuItemTapped(_ :)))
    UIMenuController.shared.menuItems = [forward, delete]
    UIMenuController.shared.update()
}

override func tableView(_ tableView: UITableView, shouldShowMenuForRowAt indexPath: IndexPath) -> Bool {
    return true
}

override func tableView(_ tableView: UITableView, performAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) {
}

override func tableView(_ tableView: UITableView, canPerformAction action: Selector, forRowAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
    return true
}

and your cell should have…

class MyCell: UITableViewCell {
    @objc func menuItemTapped(_ sender: UIMenuController) {
    }
}

Upvotes: 1

Related Questions